text:
using System;
using System.Collections.Generic;
using UnityEngine;

namespace ReadyGamerOne.Utility
{

    public static class ColliderExtension
    {
        #region Collider
        /// <summary>
        /// 向某个方向做检测，对获取到的trigger和collider做操作
        /// </summary>
        /// <param name="self"></param>
        /// <param name="dir"></param>
        /// <param name="distance"></param>
        /// <param name="layer"></param>
        /// <param name="cache"></param>
        /// <param name="onHitOther"></param>
        /// <param name="ignoreSet"></param>
        public static void CastActionNoAlloc(this Collider self, Vector3 dir, float distance, int layer,
            RaycastHit[] cache, Action<RaycastHit> onHitOther, HashSet<Collider> ignoreSet=null, Vector3? centerOffset=null)
        {
            var count = self.CastNoAlloc(dir, distance, layer, cache, centerOffset);
            for (var i = 0; i < count; i++)
            {
                var hitInfo = cache[i];
                if(ignoreSet !=null && ignoreSet.Contains(hitInfo.collider))
                    continue;
                onHitOther.Invoke(hitInfo);
            }
        }

        public static int CastNoAlloc(this Collider self, Vector3 dir, float distance, int layer, RaycastHit[] results, Vector3? centerOffset=null)
        {
            var centerPos = self.GetCenterPosition();
            if (centerOffset.HasValue)
                centerPos += centerOffset.Value;
            if (self is BoxCollider box)
            {
                return Physics.BoxCastNonAlloc(centerPos, box.size, dir.normalized, results,self.transform.rotation,distance,layer);
            }

            if (self is SphereCollider sphere)
            {
                return Physics.SphereCastNonAlloc(centerPos, sphere.radius, dir.normalized, results, distance,
                    layer);
            }

            if (self is CapsuleCollider capsule)
            {
                capsule.GetTowPoint(out var first, out var second);
                if (centerOffset.HasValue)
                {
                    return Physics.CapsuleCastNonAlloc(centerOffset.Value + first, centerOffset.Value + second, capsule.radius, dir.normalized, results, distance, layer);
                }
                return Physics.CapsuleCastNonAlloc(first, second, capsule.radius, dir.normalized, results, distance, layer);
            }

            throw new Exception($"Unexpected collider type: {self}");
        }

        public static Vector3 GetCenterPosition(this Collider self)
        {
            if (self is BoxCollider box)
            {
                return box.GetCenterPosition();
            }else if (self is SphereCollider sphere)
            {
                return sphere.GetCenterPosition();
            }else if (self is CapsuleCollider capsule)
            {
                return capsule.GetCenterPosition();
            }
            else
            {
                throw new Exception($"Unexpected collider type: {self}");
            }
        }
        public static void DrawSelfGizmos(this Collider self)
        {
            if (self is BoxCollider box)
            {
                box.DrawSelfGizmos();
            }else if (self is SphereCollider sphere)
            {
                sphere.DrawSelfGizmos();
            }else if (self is CapsuleCollider capsule)
            {
                capsule.DrawSelfGizmos();
            }
            else
            {
                throw new Exception($"Unexpected collider type: {self}");
            }
        }

        /// <summary>
        /// 计算和给定的collider至少移动什么样的距离可以脱离
        /// </summary>
        /// <param name="self"></param>
        /// <param name="colliders"></param>
        /// <param name="onOperateOtherCollider">远离其他collider的回调，（碰撞体，方向，距离）</param>
        /// <returns></returns>
        public static Vector3 ComputeCurrent(this Collider self, IEnumerable<Collider> colliders, Func<Collider,Vector3,float,Vector3> onOperateOtherCollider=null)
        {
            var ans = Vector3.zero;
            if (self.isTrigger)
                return ans;
            foreach (var collider in colliders)
            {
                if(collider.isTrigger)
                    continue;
                if (Physics.ComputePenetration(
                    self, self.transform.position, self.transform.rotation,
                    collider, collider.transform.position, collider.transform.rotation,
                    out var dir, out var distance))
                {
                    if(null!=onOperateOtherCollider)
                        ans += onOperateOtherCollider(collider,dir,distance);
                    else
                        ans += dir.normalized * distance;
                }
            }

            return ans;
        }

        /// <summary>
        /// 计算和周边碰撞体移动多少距离才能脱离碰撞，会包含自身，建议将自身加入到ignoreSet中
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer">检测的层</param>
        /// <param name="cache"></param>
        /// <param name="ignoreSet">需要忽视的碰撞体集合</param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        public static Vector3 ComputeCurrent(this Collider self,int detectLayer, Collider[] cache,
            HashSet<Collider> ignoreSet=null, Vector3? currentPosition=null)
        {
            if (self is BoxCollider box)
            {
                return box.ComputeCurrent(detectLayer,cache,ignoreSet,currentPosition);
            }else if (self is SphereCollider sphere)
            {
                return sphere.ComputeCurrent(detectLayer,cache,ignoreSet,currentPosition);
            }else if (self is CapsuleCollider capsule)
            {
                return capsule.ComputeCurrent(detectLayer,cache,ignoreSet,currentPosition);
            }
            else
            {
                throw new Exception($"Unexpected collider type: {self}");
            }
        }

        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身，但不会申请内存
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="results"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        public static int GetOverlapNoAlloc(this Collider self, int detectLayer, Collider[] results,
            Vector3? currentPosition = null)
        {
            if (self is BoxCollider box)
            {
                return box.GetOverlapNoAlloc(detectLayer,results,currentPosition);
            }else if (self is SphereCollider sphere)
            {
                return sphere.GetOverlapNoAlloc(detectLayer,results,currentPosition);
            }else if (self is CapsuleCollider capsule)
            {
                return capsule.GetOverlapNoAlloc(detectLayer,results,currentPosition);
            }
            else
            {
                throw new Exception($"Unexpected collider type: {self}");
            }
        }

        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        public static Collider[] GetOverlap(this Collider self, int detectLayer, Vector3? currentPosition = null)
        {
            if (self is BoxCollider box)
            {
                return box.GetOverlap(detectLayer,currentPosition);
            }else if (self is SphereCollider sphere)
            {
                return sphere.GetOverlap(detectLayer,currentPosition);
            }else if (self is CapsuleCollider capsule)
            {
                return capsule.GetOverlap(detectLayer,currentPosition);
            }
            else
            {
                throw new Exception($"Unexpected collider type: {self}");
            }
        }

        #endregion

        #region BoxCollider

        private static Vector3 GetCenterPosition(this BoxCollider self)
        {
            var trans = self.transform;
            return trans.position + (trans.localToWorldMatrix * self.center).ToVector3();
        }

        private static void DrawSelfGizmos(this BoxCollider self)
        {
            Gizmos.DrawWireCube(
                self.GetCenterPosition(),
                self.size);
        }

        /// <summary>
        /// 计算和周边碰撞体移动多少距离才能脱离碰撞，会包含自身，建议将自身加入到ignoreSet中
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer">检测的层</param>
        /// <param name="cache"></param>
        /// <param name="ignoreSet">需要忽视的碰撞体集合</param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static Vector3 ComputeCurrent(this BoxCollider self,int detectLayer, Collider[] cache, HashSet<Collider> ignoreSet=null, Vector3? currentPosition=null)
        {
            if(self.isTrigger)
                return Vector3.zero;
            var ans = Vector3.zero;
            var count = self.GetOverlapNoAlloc(detectLayer, cache,currentPosition);
            for (var i = 0; i < count; i++)
            {
                var other = cache[i];
                if(other.isTrigger)
                    continue;
                if(ignoreSet!=null && ignoreSet.Contains(other))
                    continue;
                if (Physics.ComputePenetration(
                    self, currentPosition ?? self.transform.position, self.transform.rotation,
                    other, other.transform.position, other.transform.rotation,
                    out var dir, out var distance))
                {
                    ans += dir.normalized * distance;
                }
            }
            return ans;
        }

        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身，但不会申请内存
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="results"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static int GetOverlapNoAlloc(this BoxCollider self, int detectLayer, Collider[] results, Vector3? currentPosition=null)
        {
            var trans = self.transform;
            return Physics.OverlapBoxNonAlloc(
                currentPosition ?? self.GetCenterPosition(),
                self.size,
                results,
                trans.rotation,
                detectLayer);
        }

        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static Collider[] GetOverlap(this BoxCollider self, int detectLayer, Vector3? currentPosition=null)
        {
            var trans = self.transform;
            return Physics.OverlapBox(
                currentPosition ?? self.GetCenterPosition(),
                self.size,
                trans.rotation,
                detectLayer);
        }

        #endregion

        #region CapsuleCollider

        private static Vector3 GetCenterPosition(this CapsuleCollider self)
        {
            var trans = self.transform;
           return trans.position + (trans.localToWorldMatrix * self.center).ToVector3();
        }

        public static float GetRealHeight(this CapsuleCollider self)
        {
            return Mathf.Max(2 * self.radius, self.height);
        }

        public static float GetMiddleLength(this CapsuleCollider self)
        {
            return Mathf.Max(0, self.height - 2 * self.radius);
        }

        public static void GetTowPoint(this CapsuleCollider self, out Vector3 first, out Vector3 second,Vector3? currentPosition=null)
        {
            var trans = self.transform;
            var pos = currentPosition ?? self.GetCenterPosition();
            var halfHeight = self.GetMiddleLength() / 2;
            first = Vector3.zero;
            second=Vector3.zero;
            switch (self.direction)
            {
                case 0:// x axis
                    first = pos - trans.right * halfHeight;
                    second = pos + trans.right * halfHeight;
                    break;
                case 1:// y axis
                    first = pos - trans.up * halfHeight;
                    second = pos + trans.up * halfHeight;
                    break;
                case 2:// z axis
                    first = pos - trans.forward * halfHeight;
                    second = pos + trans.forward * halfHeight;
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        private static void DrawSelfGizmos(this CapsuleCollider self)
        {
            self.GetTowPoint(out var first, out var second);
            Gizmos.DrawWireSphere(first, self.radius);
            Gizmos.DrawWireSphere(second, self.radius);

        }
        /// <summary>
        /// 计算和周边碰撞体移动多少距离才能脱离碰撞，会包含自身，建议将自身加入到ignoreSet中
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer">检测的层</param>
        /// <param name="cache"></param>
        /// <param name="ignoreSet">需要忽视的碰撞体集合</param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static Vector3 ComputeCurrent(this CapsuleCollider self, int detectLayer, Collider[] cache, HashSet<Collider> ignoreSet=null, Vector3? currentPosition=null)
        {
            if(self.isTrigger)
                return Vector3.zero;
            var ans = Vector3.zero;
            var count = self.GetOverlapNoAlloc(detectLayer, cache, currentPosition);
            for (var i = 0; i < count; i++)
            {
                var other = cache[i];
                if(other.isTrigger)
                    continue;
                if(ignoreSet!=null && ignoreSet.Contains(other))
                    continue;
                if (Physics.ComputePenetration(
                    self, currentPosition ?? self.transform.position, self.transform.rotation,
                    other, other.transform.position, other.transform.rotation,
                    out var dir, out var distance))
                {
                    ans += dir.normalized * distance;
                }
            }
            return ans;
        }

        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身，但不会申请内存
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="results"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static int GetOverlapNoAlloc(this CapsuleCollider self, int detectLayer, Collider[] results, Vector3? currentPosition=null)
        {
            self.GetTowPoint(out var first, out var second, currentPosition);

            return Physics.OverlapCapsuleNonAlloc(first, second,
                self.radius, results, detectLayer);
        }

        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static Collider[] GetOverlap(this CapsuleCollider self, int detectLayer, Vector3? currentPosition=null)
        {
            self.GetTowPoint(out var first, out var second, currentPosition);
            return Physics.OverlapCapsule(first, second,
                self.radius, detectLayer);
        }

        #endregion

        #region SphereCollider

        private static Vector3 GetCenterPosition(this SphereCollider self)
        {
            var trans = self.transform;
            return trans.position + (trans.localToWorldMatrix * self.center).ToVector3();
        }

        private static void DrawSelfGizmos(this SphereCollider self)
        {
            Gizmos.DrawWireSphere(
                self.GetCenterPosition(),
                self.radius);
        }
        /// <summary>
        /// 计算和周边碰撞体移动多少距离才能脱离碰撞，会包含自身，建议将自身加入到ignoreSet中
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer">检测的层</param>
        /// <param name="cache"></param>
        /// <param name="ignoreSet">需要忽视的碰撞体集合</param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static Vector3 ComputeCurrent(this SphereCollider self, int detectLayer, Collider[] cache, HashSet<Collider> ignoreSet=null, Vector3? currentPosition=null)
        {
            if(self.isTrigger)
                return Vector3.zero;
            var ans = Vector3.zero;
            var count = self.GetOverlapNoAlloc(detectLayer, cache,currentPosition);
            for (var i = 0; i < count; i++)
            {
                var other = cache[i];
                if(other.isTrigger)
                    continue;
                if(ignoreSet!=null && ignoreSet.Contains(other))
                    continue;
                if (Physics.ComputePenetration(
                    self, currentPosition ?? self.transform.position, self.transform.rotation,
                    other, other.transform.position, other.transform.rotation,
                    out var dir, out var distance))
                {
                    ans += dir.normalized * distance;
                }
            }
            return ans;
        }
        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身，但不会申请内存
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="results"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static int GetOverlapNoAlloc(this SphereCollider self, int detectLayer, Collider[] results, Vector3? currentPosition=null)
        {
            return Physics.OverlapSphereNonAlloc(
                currentPosition ?? self.GetCenterPosition(),
                self.radius,
                results,
                detectLayer);
        }
        /// <summary>
        /// 获取有交叉的所有碰撞体和触发器，会包括自身
        /// </summary>
        /// <param name="self"></param>
        /// <param name="detectLayer"></param>
        /// <param name="currentPosition"></param>
        /// <returns></returns>
        private static Collider[] GetOverlap(this SphereCollider self,int detectLayer, Vector3? currentPosition=null)
        {
            return Physics.OverlapSphere(
                 currentPosition ?? self.GetCenterPosition(),
                self.radius,
                detectLayer);
        }

        #endregion

    }
}


text:
fileFormatVersion: 2
guid: 3e0beb3bd573470089db5cdd22d7b24e
timeCreated: 1570508765

mime_type: text/plain


text:
using System;
using UnityEngine;
using UnityEngine.Assertions;

namespace ReadyGamerOne.Utility
{
    public static class ComponentExtension
    {
        public static T GetComponent<T>(this Component self, string transformPath)
            where T : Component
        {
            if (!self)
                return null;
            var temp = self.transform.Find(transformPath);
            if (!temp)
            {
                throw new Exception($"{self.gameObject.name} 获取组建失败：" + transformPath);
            }
            return temp.GetComponent<T>();
        }
        public static T[] GetComponents<T>(this Component self, string transformPath)
            where T : Component
        {
            if (!self)
                return null;
            var temp = self.transform.Find(transformPath);
            if (!temp)
            {
                throw new Exception($"{self.gameObject.name} 获取组建失败：" + transformPath);
            }
            return temp.GetComponents<T>();
        }
        public static T GetComponentInChildren<T>(this Component self, string transformPath)
            where T : Component
        {
            if (!self)
                return null;
            var temp = self.transform.Find(transformPath);
            if (!temp)
            {
                throw new Exception($"{self.gameObject.name} 获取组建失败：" + transformPath);
            }
            return temp.GetComponentInChildren<T>();
        }
        public static T[] GetComponentsInChildren<T>(this Component self, string transformPath)
            where T : Component
        {
            if (!self)
                return null;
            var temp = self.transform.Find(transformPath);
            if (!temp)
            {
                throw new Exception($"{self.gameObject.name} 获取组建失败：" + transformPath);
            }
            return temp.GetComponentsInChildren<T>();
        }

        public static T GetOrAddComponent<T>(this Component self)
            where T:Component
        {
            var ans = self.GetComponent<T>();
            if (!ans)
                ans = self.gameObject.AddComponent<T>();
            return ans;
        }

        public static Component GetOrAddComponent(this Component self, Type componentType)
        {
            var com = self.GetComponent(componentType);
            if (!com)
                com = self.gameObject.AddComponent(componentType);
            Assert.IsTrue(com);
            return com;
        }
    }
}


text:
fileFormatVersion: 2
guid: 386d8bbb438b48aea5b839adc8a433b0
timeCreated: 1588410783


text:
using System.Collections.Generic;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class CameraExtension
    {
        /// <summary>
        /// 获取camera在一定距离前四个角世界坐标
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        public static List<Vector3> GetCorners(this Camera camera, float distance)
        {
            var theta = camera.fieldOfView/2;

            var halfHeight = distance * Mathf.Tan(Mathf.Deg2Rad * theta);

            var halfWidth = halfHeight * camera.aspect;

            var transform = camera.transform;
            var center = transform.position + transform.forward * distance;

            var right = transform.right;
            var up = transform.up;
            return  new List<Vector3>
            {
                center + right * (-halfWidth) + up * (halfHeight),
                center + right * (halfWidth) + up * (halfHeight),
                center + right * (halfWidth) + up * (-halfHeight),
                center + right * (-halfWidth) + up * (-halfHeight)
            };

        }
    }
}


text:
fileFormatVersion: 2
guid: f735256bc1ec4445b35f38bbb71d58e7
timeCreated: 1570151908


text:
fileFormatVersion: 2
guid: bec31d338a0d472c85c762de40532901
timeCreated: 1578795742


text:
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public static class TransformExtension
    {
        /// <summary>
        /// 获取一个坐标在某一个Transform视角里的位置，x和y范围都是（-2，2）
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="targetPos"></param>
        /// <returns></returns>
        public static Vector2 GetViewVector2(this Transform transform, Vector3 targetPos)
        {
            var dir = targetPos - transform.position;

            var dirHor = new Vector3(dir.x, 0, dir.z);
            var dirVer=new Vector3(0,dir.y,Mathf.Sqrt(dir.x*dir.x+dir.z*dir.z));

            var isRight = Vector3.Cross(dirHor, transform.forward).y<0;
            var horDegree = Vector3.Angle(dirHor, transform.forward);
            var horValue = ( isRight ? 1 : -1 )* horDegree / 90;

            var verticalDegree = Vector3.Angle(dirVer, transform.forward);
            var verValue = (targetPos.y > transform.position.y ? 1 : -1) * verticalDegree / 90.0f;

            return new Vector2(horValue, verValue);
        }

        public static void DestroyChildrenImmediate(this Transform transform)
        {
            while (transform.childCount > 0)
            {
                Object.DestroyImmediate(transform.GetChild(0).gameObject);
            }
        }
    }
}


text:
namespace ReadyGamerOne.Utility
{
    public static class IntExtension
    {
        /// <summary>
        /// 获取Int数从右往左第Index位上的数字，只会返回 0 或 1
        /// </summary>
        /// <param name="self"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static int GetNumAtBinary(this int self, int index)
        {
            return (((self & (1 << index)) >> index) == 1) ? 1 : 0;
        }
    }
}


text:
fileFormatVersion: 2
guid: fb22849a31dc4f6ca01e4fdd4d877918
timeCreated: 1589464531


text:
using System;
using System.Reflection;

namespace ReadyGamerOne.Utility
{
    public static class TypeExtension
    {
        /// <summary>
        /// 获取Type的特性
        /// </summary>
        /// <param name="type"></param>
        /// <param name="inherit"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T GetAttribute<T>(this Type type, bool inherit = true)
            where T : System.Attribute
        {
            foreach (var attribute in type.GetCustomAttributes(inherit))
            {
                if (attribute is T)
                    return (T)attribute;
            }

            return null;
        }

        /// <summary>
        /// 根据名字获取父类中的方法
        /// </summary>
        /// <param name="self"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static MethodInfo GetMethodFromBase(this Type self, string methodName)
        {
            var temp = self;
            while (null!=temp)
            {
                var method = temp.GetMethod(methodName);
                if (method != null)
                    return method;
                temp = temp.BaseType;
            }

            return null;
        }
    }
}


text:
fileFormatVersion: 2
guid: 8242c870dc9c47bd99c3d3d2076a8435
timeCreated: 1576044679


text:
using System.IO;

namespace ReadyGamerOne.Utility
{
    public static class DirectoryInfoExtension
    {
        /// <summary>
        /// 判断某目录下是否有Unity有效文件
        /// </summary>
        /// <param name="directory"></param>
        /// <returns></returns>
        public static bool HasValuableFiles(this DirectoryInfo directory)
        {
            foreach (var fileInfo in directory.GetFiles())
            {
                if (!fileInfo.FullName.EndsWith(".meta"))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// 获取以Key为标识的特殊路径
        /// </summary>
        /// <param name="dir"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetSpecialPath(this DirectoryInfo dir, string key)
        {
            var path = "";
            while (dir.Name.StartsWith(key))
            {
                path = "/" + dir.Name.GetAfterSubstring(key) +  path;
                dir = dir.Parent;
            }

            return path;
        }
    }
}


text:
using System;
using UnityEngine;
using UnityEngine.Assertions;

namespace ReadyGamerOne.Utility
{
    public static class GameObjectExtension
    {
        public static int GetUnityDetectLayer(this GameObject self)
        {
            var layer=0;
            for (var i = 0; i < 32; i++)
            {
                var ignore = Physics.GetIgnoreLayerCollision(i, self.layer);
                if(!ignore)
                    layer |= 1 << i;
            }

            return layer;
        }
        public static T GetOrAddComponent<T>(this GameObject self)
            where T:Component
        {
            var ans = self.GetComponent<T>();
            if (!ans)
                ans = self.AddComponent<T>();
            return ans;
        }

        public static Component GetOrAddComponent(this GameObject self, Type componentType)
        {
            var com = self.GetComponent(componentType);
            if (!com)
                com = self.AddComponent(componentType);
            Assert.IsTrue(com);
            return com;
        }
    }
}


text:
using System;

namespace ReadyGamerOne.Utility
{
    public static class ByteArrayExtension
    {
        /// <summary>
        /// 以小尾巴形式在byte数组写入ushort类型数据
        /// </summary>
        /// <param name="self"></param>
        /// <param name="offset">偏移</param>
        /// <param name="value"></param>
        public static void WriteUShortLe(this byte[] self,ushort value, int offset=0)
        {
            //此函数运行结果是小尾巴，还是大尾巴，取决于系统
            var byteValue = BitConverter.GetBytes(value);
            if (!BitConverter.IsLittleEndian)
            {
                Array.Reverse(byteValue);
            }

            Array.Copy(byteValue, 0, self, offset, byteValue.Length);

        }

        /// <summary>
        /// 以小尾巴形式在byte数组写入uint类型数据
        /// </summary>
        /// <param name="self"></param>
        /// <param name="value"></param>
        /// <param name="offset"></param>
        public static void WriteUIntLe(this byte[] self,  uint value, int offset=0)
        {
            //此函数运行结果是小尾巴，还是大尾巴，取决于系统
            var byteValue = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(byteValue);
            }

            Array.Copy(byteValue, 0, self, offset, byteValue.Length);

        }

        /// <summary>
        /// 将另一个byte数组写入到当前数组
        /// </summary>
        /// <param name="self"></param>
        /// <param name="value"></param>
        /// <param name="offset"></param>
        public static void WriteBytes(this byte[] self, byte[] value, int offset = 0)
        {
            Array.Copy(value, 0, self, offset, value.Length);
        }

        /// <summary>
        /// 以小尾巴的形式从数组中读取出一个两字节数据（ushort)
        /// </summary>
        /// <param name="self"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public static ushort ReadUShortLe(this byte[] self, int offset = 0)
        {
            var ret = self[offset] | (self[offset+1] << 8);
            return (ushort) ret;
        }
    }

}


text:
using System;

namespace ReadyGamerOne.Utility
{
    public static class StringExtension
    {
        public static int FindFirstSubstring(this string sour, string substring)
        {
            var length = sour.Length;
            var times = length - substring.Length + 1;
            if (times == 0)
                throw new Exception("substring 比 sour 短，怎么找Index？");

            for (var i = 0; i < times; i++)
            {
                var temp = sour.Substring(i, length - i);
                if (temp.StartsWith(substring))
                    return i;
            }
            return -1;
        }

        public static string GetAfterSubstring(this string sour, string substring)
        {
            var index = sour.FindFirstSubstring(substring);
            index += substring.Length;
            return sour.Substring(index, sour.Length - index);
        }

        public static string GetBeforeSubstring(this string sour, string substring)
        {
            var index = sour.FindFirstSubstring(substring);
            return sour.Substring(0, index);
        }

        public static string GetAfterLastChar(this string sour, char c)
        {
            var index = sour.LastIndexOf(c);
            return sour.Substring(index+1,sour.Length-(1+index));
        }
    }
}


text:
fileFormatVersion: 2
guid: 23bf9416c6c344dda72e89af53f382f5
timeCreated: 1576692028


text:
fileFormatVersion: 2
guid: deeeb89085c84b8cbeccd7ccbf751e51
timeCreated: 1611147202


text:
fileFormatVersion: 2
guid: 6a2abac318f14dc0b6c243d0cf7127f7
timeCreated: 1574423381


text:
using System;
using UnityEngine;

namespace ReadyGamerOne.Utility
{
    public enum AxisIgnore
    {
        X,
        Y,
        Z
    }
    public static class VectorExtension
    {
        public static Vector3 ToVector3(this Vector4 self)
        {
            return new Vector3(self.x, self.y, self.z);
        }
        public static Vector2 ToVector2(this Vector3 self)
        {
            return new Vector2(self.x, self.y);
        }

        public static Vector3 ToVector3(this Vector2 self, float zValue = 0f)
        {
            return new Vector3(self.x, self.y, zValue);
        }

        /// <summary>
        /// 判断两个Vector是否在容错范围内相等
        /// </summary>
        /// <param name="self"></param>
        /// <param name="another"></param>
        /// <param name="tolerance"></param>
        /// <returns></returns>
        public static bool EqualsTo(this Vector3 self, Vector3 another, float tolerance = 0.01f)
        {
            return Mathf.Abs(self.x - another.x) < tolerance
                   && Mathf.Abs(self.y - another.y) < tolerance
                   && Mathf.Abs(self.z - another.z) < tolerance;
        }

        public static bool EqualsTo(this Vector2 self, Vector2 another, float tolerance = 0.01f)
        {
            return Mathf.Abs(self.x - another.x) < tolerance
                   && Mathf.Abs(self.y - another.y) < tolerance;
        }

        /// <summary>
        /// 将一个Vector3向量的XY分量旋转角度制degree
        /// </summary>
        /// <param name="self"></param>
        /// <param name="degree"></param>
        /// <returns></returns>
        public static Vector3 RotateDegree(this Vector3 self, float degree, AxisIgnore axisIgnore)
        {
            var sin = Mathf.Sin(degree * Mathf.Deg2Rad);
            var cos = Mathf.Cos(degree * Mathf.Deg2Rad);

            if (axisIgnore == AxisIgnore.X)
                return new Vector3(
                    self.x,
                    self.z*cos -self.y*sin,
                    self.z*sin+self.y*cos);

            if (axisIgnore == AxisIgnore.Y)
                return new Vector3(
                    self.x*cos -self.z*sin,
                    self.y,
                    self.x*sin+self.z*cos);

            if(axisIgnore==AxisIgnore.Z)
                return new Vector3(
                    self.x*cos -self.y*sin,
                    self.x*sin+self.y*cos,
                    self.z);

            throw new Exception($"Unexpected AxisIgnore:{axisIgnore}");
        }

        /// <summary>
        /// 交换x和y
        /// </summary>
        /// <param name="self"></param>
        /// <returns></returns>
        public static Vector2 Switch(this Vector2 self)
        {
            return new Vector2(self.y, self.x);
        }

        public static Vector2 RotateDegree(this Vector2 self, float degree)
        {
            var sin = Mathf.Sin(-degree * Mathf.Deg2Rad);
            var cos = Mathf.Cos(-degree * Mathf.Deg2Rad);

            return new Vector2(
                self.x*cos -self.y*sin,
                self.x*sin+self.y*cos
            );
        }
    }
}


text:
using UnityEngine;
using UnityEngine.UI;

namespace ReadyGamerOne.Utility
{
    public static class AlphaExtension
    {
        public static Color SetAlpha(this Color color, float alpha)
        {
            var okAlpha = Mathf.Clamp(alpha, 0, 1);
            return new Color(color.r, color.g, color.b, okAlpha);

        }

        public static void SetAlpha(this Image image, float alpha)
        {

            var okAlpha = Mathf.Clamp(alpha, 0, 1);
            var color = image.color;
            color = new Color(color.r, color.g, color.b, okAlpha);
            image.color = color;
        }

    }
}


text:
fileFormatVersion: 2
guid: 2133454541de4a8e83f074bdf8433c75
timeCreated: 1574605329


text:
fileFormatVersion: 2
guid: d7ab84f8b4f36ce4998fce0be01c93d4
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using System.Collections.Generic;
using System.Reflection;
using ReadyGamerOne.Global;
using UnityEditor;
using UnityEngine;
using FileUtil = ReadyGamerOne.Utility.FileUtil;

namespace ReadyGamerOne.Editor
{
    public class ToolWindow:EditorWindow
    {

        [MenuItem("ReadyGamerOne/工具仓")]
        static void ShowWindow()
        {
            var win = GetWindow<ToolWindow>();
            win.titleContent = new GUIContent("工具仓");
            win.Show();
        }

        private string RootNamespaceKey = "RootNamespace";

        private string RootNamespace = "Root";
        private string UiNamespace = "View";
        private string ConstNamespace = "Const";
        private string DataNamespace = "Data";
        private string AutoGeneratedDirName = "Auto";
        private string ScriptNamespace="Script";

        private List<MethodInfo> _methodInfos = new List<MethodInfo>();
        private Vector2 pos;
        private List<string> names=new List<string>();
        private int index = 0;
        private GUIStyle _style;

        private void OnEnable()
        {
            RootNamespace = EditorPrefs.GetString(RootNamespaceKey, "Root");

            _style=new GUIStyle
            {
                fontSize = 15,
                fontStyle = FontStyle.BoldAndItalic,
                alignment = TextAnchor.MiddleCenter
            };

            var types = Assembly.GetAssembly(typeof(IEditorTools)).GetTypes();
            foreach (var type in types)
            {
                if(type.IsInterface)
                    continue;
                if (typeof(IEditorTools).IsAssignableFrom(type))
                {
                    var func = type.GetMethod("OnToolsGUI",
                        BindingFlags.IgnoreCase
                        | BindingFlags.NonPublic
                        | BindingFlags.Static);
                    if(func==null)
                        continue;
                    var name=type.GetField("Title",
                        BindingFlags.IgnoreCase
                        | BindingFlags.NonPublic
                        | BindingFlags.Static);
                    _methodInfos.Add(func);
                    names.Add((string)name?.GetValue(null)?? type.Name);
                }
            }
        }

        private void OnDisable()
        {
            _methodInfos.Clear();
            names.Clear();
            EditorPrefs.SetString(RootNamespaceKey, RootNamespace);
        }

        private void OnSelectionChange()
        {
            Repaint();
        }

        public void OnGUI()
        {

            UiNamespace = string.IsNullOrEmpty(UiNamespace) ? "View" : UiNamespace;
            ConstNamespace = string.IsNullOrEmpty(ConstNamespace) ? "Const" : ConstNamespace;
            DataNamespace = string.IsNullOrEmpty(DataNamespace) ? "Data" : DataNamespace;
            AutoGeneratedDirName = string.IsNullOrEmpty(AutoGeneratedDirName) ? "Auto" : AutoGeneratedDirName;
            ScriptNamespace = string.IsNullOrEmpty(ScriptNamespace) ? "Script" : ScriptNamespace;

            EditorGUILayout.Space();
            EditorGUILayout.Space();
            EditorGUILayout.LabelField("ToolsWindow", _style);
            EditorGUI.BeginChangeCheck();
            RootNamespace = EditorGUILayout.DelayedTextField("RootNamespace", RootNamespace);
            if (EditorGUI.EndChangeCheck())
            {
                EditorPrefs.SetString(RootNamespaceKey, RootNamespace);
            }

            UiNamespace = EditorGUILayout.TextField("UiNamespace", UiNamespace);
            ConstNamespace = EditorGUILayout.TextField("ConstNamespace", ConstNamespace);
            DataNamespace = EditorGUILayout.TextField("DataNamespace", DataNamespace);
            AutoGeneratedDirName = EditorGUILayout.TextField("AutoGeneratedDirName", AutoGeneratedDirName);
            ScriptNamespace = EditorGUILayout.TextField("ScriptNamespace", ScriptNamespace);

            EditorGUILayout.Space();

            if (RootNamespace == "Root" || string.IsNullOrEmpty(RootNamespace))
            {
                EditorGUILayout.HelpBox("忘了设置命名空间了！", MessageType.Error);
                return;
            }

            if (GUILayout.Button("创建相关文件夹"))
            {
                var root = Application.dataPath + "/" + RootNamespace;
                FileUtil.CreateFolder(root);
                FileUtil.CreateFolder(root + "/" + UiNamespace);
                FileUtil.CreateFolder(root + "/" + ConstNamespace);
                FileUtil.CreateFolder(root + "/" + DataNamespace);
                FileUtil.CreateFolder(root + "/" + ScriptNamespace);
                AssetDatabase.Refresh();
                Debug.Log("生成完毕");
            }

//            if (GUILayout.Button("创建GameMgr"))
//            {
//                var go = new GameObject("GameMgr");
//                Debug.Log(RootNamespace+"."+ScriptNamespace +"." + RootNamespace + "Mgr");
//                var type = Type.GetType(RootNamespace+"."+ScriptNamespace +"." + RootNamespace + "Mgr");
//                Debug.Log(type);
//                go.AddComponent(type);
//            }

            EditorGUILayout.Space();
            pos = GUILayout.BeginScrollView(pos);

            var arr = new[] {RootNamespace, UiNamespace, ConstNamespace, DataNamespace, AutoGeneratedDirName,ScriptNamespace};
            if (names.Count > 0)
            {
                index = GUILayout.Toolbar(index, names.ToArray());

                _methodInfos[index].Invoke(null,arr);

            }else
                GUILayout.Label("没有内容。。。");

            GUILayout.EndScrollView();
        }
    }
}


text:
fileFormatVersion: 2
guid: 7110bf4c3ccf4f3a9153345b27c5effb
timeCreated: 1581496297


text:
using ReadyGamerOne.Utility;
using UnityEngine.Assertions;

namespace ReadyGamerOne.Network
{
    public static class TcpProtocol
    {
        private const int HeadSize = 2;

        /// <summary>
        /// 将byte数组打包
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Pack(byte[] data)
        {
            var len = data.Length;

            Assert.IsTrue(len <= 65535 - 2);

            var cmdLen = len + HeadSize;
            var cmd = new byte[cmdLen];
            cmd.WriteUShortLe((ushort)cmdLen);
            cmd.WriteBytes(data, HeadSize);

            return cmd;
        }

        /// <summary>
        /// 读取Tcp包头信息
        /// </summary>
        /// <param name="data"></param>
        /// <param name="dataLen"></param>
        /// <param name="pkgSize"></param>
        /// <param name="headSize"></param>
        /// <returns></returns>
        public static bool ReadHeader(byte[] data, int dataLen, out int pkgSize, out int headSize, int offset=0)
        {
            pkgSize = 0;
            headSize = 0;
            if (dataLen < 2)
                return false;

            pkgSize = data.ReadUShortLe(offset);
            headSize = 2;
            return true;
        }
    }
}


text:
fileFormatVersion: 2
guid: 9a18419dd1e843c5bcec23293c8a6046
timeCreated: 1605777022


text:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using PurificationPioneer.Scriptable;
using ReadyGamerOne.Utility;
using UnityEngine;
using UnityEngine.Assertions;

namespace ReadyGamerOne.Network
{
    public class UdpHelper
    {
        public bool IsValid => clientSocket != null;
        public int LocalUdpPort { get; private set; }
        public string LocalIp { get; private set; }
        public string TargetIp { get; private set; }
        public int TargetPort { get; private set; }

        #region private

        private Socket clientSocket;
        private Thread recvThread;
        private byte[] udpRecvBuff;
        private IPEndPoint targetIpEndPoint;

        private Action<byte[], int, int> onRecvCmd;
        private Action<Exception> onException;
        private Func<bool> ifEnableSocketLog = () => true;

        private bool shouldKill = false;

        #endregion

        public UdpHelper(
            string targetIp,
            int targetPort,
            int localPort,
            Action<Exception> onException,
            Action<byte[],int,int> onRecvCmd,
            int maxUdpPackageSize,
            Func<bool> enableSocketLog=null,
            Action onFinishedSetup=null,
            byte[] initBytes=null)
        {
            Assert.IsNotNull(onException);
            Assert.IsNotNull(onRecvCmd);

            if (null != enableSocketLog)
                this.ifEnableSocketLog = enableSocketLog;

            this.onException = onException;
            this.onRecvCmd = onRecvCmd;
            this.udpRecvBuff=new byte[maxUdpPackageSize];
            this.LocalUdpPort = localPort;
            this.TargetIp = targetIp;
            this.TargetPort = targetPort;
            this.targetIpEndPoint = new IPEndPoint(IPAddress.Parse(TargetIp), TargetPort);

            NetUtil.SetupUdp(
                targetIpEndPoint,
                LocalUdpPort,
                RecvThread,
                (localIp, socket, recvThread) =>
                {
                    this.LocalIp = localIp;

                    //创建udpSocket
                    try
                    {
                        this.clientSocket = socket;
                        this.recvThread = recvThread;
        #if DebugMode
                        if(ifEnableSocketLog())
                            Debug.Log($"Udp[local-{this.LocalIp}:{LocalUdpPort}] 开始接收");
        #endif
                        onFinishedSetup?.Invoke();
                    }
                    catch (Exception e)
                    {
                        this.onException(e);
                    }
                });
        }

        /// <summary>
        /// 关闭接收器
        /// </summary>
        public void CloseReceiver()
        {
            shouldKill = true;
            udpRecvBuff = null;

            if (null != recvThread)
            {
                try
                {
                    recvThread.Interrupt();
                    recvThread.Abort();
                }
                catch (Exception e)
                {
#if DebugMode
                    if (GameSettings.Instance.EnableSocketClosingException)
                    {
                        Debug.Log($"[Udp.recvThread.Abort]{e}");
                    }
#endif
                }
            }
            recvThread = null;

            if (this.clientSocket != null && this.clientSocket.Connected)
            {
                try
                {
                    this.clientSocket.Close();
                }
                catch (Exception e)
                {
#if DebugMode
                    if (GameSettings.Instance.EnableSocketClosingException)
                    {
                        Debug.Log($"[Udp.clientSocket.Close]{e}");
                    }
#endif
                }
            }
            this.clientSocket = null;

#if DebugMode
            if(ifEnableSocketLog())
                Debug.Log($"Udp[local-{this.LocalIp}:{LocalUdpPort}] 关闭连接");
#endif
        }

        /// <summary>
        /// 发送内容
        /// </summary>
        /// <param name="content"></param>
        public void Send(byte[] content)
        {
            try
            {
                this.clientSocket.BeginSendTo(
                    content,
                    0,
                    content.Length,
                    SocketFlags.None,
                    targetIpEndPoint,
                    OnUdpSend,
                    this.clientSocket);

#if DebugMode
                if(ifEnableSocketLog())
                    Debug.Log($"Udp[Local:{this.LocalIp}:{LocalUdpPort}->{TargetIp}:{TargetPort}] 发送数据[{content.Length}]");
#endif
            }
            catch (Exception e)
            {
                onException(e);
            }
        }

        #region Private

        /// <summary>
        /// UDP收数据线程
        /// </summary>
        private void RecvThread()
        {
            while (true)
            {
                if (shouldKill)
                {

                    try
                    {
                        Thread.CurrentThread.Abort();
                    }
                    catch (Exception e)
                    {
#if DebugMode
                        if (GameSettings.Instance.EnableSocketClosingException)
                        {
                            Debug.Log($"[Udp.shouldKill.Abort]{e}");
                        }
#endif
                    }
                    break;
                }
                EndPoint remote = new IPEndPoint(IPAddress.Any, 0);
                try
                {
                    var receivedLength = this.clientSocket.ReceiveFrom(this.udpRecvBuff,ref remote);

#if DebugMode
                    if(ifEnableSocketLog())
                        Debug.Log($"Udp[local-{this.LocalIp}:{LocalUdpPort}] 收到数据[{receivedLength}]");
#endif

                    this.onRecvCmd(this.udpRecvBuff, 0, receivedLength);
                }
                catch (Exception e)
                {
                    if(this.onException!=null)
                        this.onException(e);
                    break;
                }
            }
        }

        private void OnUdpSend(IAsyncResult ar)
        {
            try
            {
                var client = ar.AsyncState as Socket;
                Assert.IsNotNull(client);
                client.EndSendTo(ar);
            }
            catch (Exception e)
            {
                onException(e);
            }
        }

        #endregion
    }
}


text:
fileFormatVersion: 2
guid: c9a4a60404e54b3cb11fb0fd9b10af60
timeCreated: 1605777247


text:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using PurificationPioneer.Scriptable;
using UnityEngine;
using UnityEngine.Assertions;

namespace ReadyGamerOne.Network
{
    public class TcpHelper
    {
            #region Private

            private readonly int MaxBufferSize;

            private Socket clientSocket;

            private int usedBufferLength;
            private byte[] longPkg;
            private int longPkgSize;

            private Thread recvThread;
            private byte[] receiveBuffer;

            private Action<byte[], int, int> onRecvCmd;
            private Action<Exception> onException;
            private Func<bool> ifEnableSocketLog = () => true;

            private bool shouldKill = false;

            #endregion

            public string Ip { get; }
            public int Port { get; }

            public bool IsValid => clientSocket != null && clientSocket.Connected;

            public TcpHelper(string tcpServerIp, int tcpPort, Action<Exception> onException, Action<byte[],int,int> onRecvCmd, int maxTcpBufferSize, int maxWaitTime, Func<bool> enableSocketLog=null)
            {
                Assert.IsFalse(string.IsNullOrEmpty(tcpServerIp));
                Assert.IsTrue(0 != tcpPort);
                Assert.IsNotNull(onException);
                Assert.IsNotNull(onRecvCmd);

                if (null != enableSocketLog)
                    this.ifEnableSocketLog = enableSocketLog;

                this.Ip = tcpServerIp;
                this.Port = tcpPort;

                this.onException = onException;
                this.onRecvCmd = onRecvCmd;
                this.MaxBufferSize = maxTcpBufferSize;

                clientSocket = new Socket(
                    AddressFamily.InterNetwork,
                    SocketType.Stream,
                    ProtocolType.Tcp);

                var ip = IPAddress.Parse(tcpServerIp);
                var ipEndPoint = new IPEndPoint(ip, tcpPort);
                var result = clientSocket.BeginConnect(
                    ipEndPoint,
                    OnConnectCallback,
                    clientSocket);
                var success = result.AsyncWaitHandle.WaitOne(maxWaitTime, true);
                if (!success)
                {
                    this.onException?.Invoke(new Exception("链接超时"));
                }
            }

            /// <summary>
            /// 关闭接收器
            /// </summary>
            public void CloseReceiver()
            {
                shouldKill = true;
                longPkg = null;
                receiveBuffer = null;
                if (null != recvThread)
                {
                    try
                    {
                        recvThread.Interrupt();
                        recvThread.Abort();
                    }
                    catch (Exception e)
                    {
#if DebugMode
                        if (GameSettings.Instance.EnableSocketClosingException)
                        {
                            Debug.Log($"[Tcp.recvThread.Abort]{e}");
                        }
#endif
                    }
                }
                recvThread = null;

                if (this.clientSocket != null && this.clientSocket.Connected)
                {
                    try
                    {
                        this.clientSocket.Close();
                    }
                    catch (Exception e)
                    {
#if DebugMode
                        if (GameSettings.Instance.EnableSocketClosingException)
                        {
                            Debug.Log($"[Tcp.clientSocket.Close]{e}");
                        }
#endif
                    }
                }
                this.clientSocket = null;
#if DebugMode
                if(ifEnableSocketLog())
                    Debug.Log($"Tcp[{Ip}:{Port}] 关闭连接");
#endif
            }

            /// <summary>
            /// 发送内容
            /// </summary>
            /// <param name="content"></param>
            public void Send(byte[] content)
            {
                try
                {
                    this.clientSocket.BeginSend(
                        content,
                        0,
                        content.Length,
                        SocketFlags.None,
                        OnTcpSend, this.clientSocket);
#if DebugMode
                    if(ifEnableSocketLog())
                        Debug.Log($"Tcp[{Ip}:{Port}] 发送数据[{content.Length}]");
#endif
                }
                catch (Exception e)
                {
                    onException(e);
                }
            }

            #region Private

            private void OnTcpSend(IAsyncResult ar)
            {
                try
                {
                    var client = ar.AsyncState as Socket;
                    Assert.IsNotNull(client);
                    client?.EndSend(ar);
                }
                catch (Exception e)
                {
                    onException(e);
                }
            }

            private void BeginReceive()
            {
                this.receiveBuffer=new byte[this.MaxBufferSize];
                this.recvThread=new Thread(RecvThread);
                this.recvThread.Start();
#if DebugMode
                if(ifEnableSocketLog())
                    Debug.Log($"Tcp[{Ip}:{Port}] 开始接收");
#endif
            }

            /// <summary>
            /// Tcp连接成功回调
            /// </summary>
            /// <param name="ar"></param>
            private void OnConnectCallback(IAsyncResult ar)
            {
                try
                {
                    var socket = ar.AsyncState as Socket;
                    Assert.IsNotNull(socket);
                    socket.EndConnect(ar);
#if DebugMode
                    if(ifEnableSocketLog())
                        Debug.Log($"Tcp[{Ip}:{Port}] 建立连接");
#endif
                    BeginReceive();
                }
                catch (Exception e)
                {
                    this.onException(e);
                }
            }

            private void OnRecvTcpData()
            {
                var dataBuffer = this.longPkg ?? this.receiveBuffer;

                while (this.usedBufferLength > 0)
                {
                    if (!TcpProtocol.ReadHeader(dataBuffer, dataBuffer.Length,
                        out var pkgSize,
                        out var headerSize))
                    {
                        break;
                    }

                    if (this.usedBufferLength < pkgSize)
                    {
                        break;
                    }

                    var rawDataStart = headerSize;
                    var rawDataLen = pkgSize - headerSize;
#if DebugMode
                    if(ifEnableSocketLog())
                        Debug.Log($"Tcp[{Ip}:{Port}] 接收到数据包，[{pkgSize}]");
#endif
                    this.onRecvCmd(dataBuffer, rawDataStart, rawDataLen);

                    if (this.usedBufferLength > pkgSize)
                    {//粘包
                        Array.Copy(dataBuffer, pkgSize, dataBuffer, 0, this.usedBufferLength - pkgSize);
                    }

                    this.usedBufferLength -= pkgSize;

                    if (this.usedBufferLength == 0 && this.longPkg != null)
                    {
                        this.longPkg = null;
                        this.longPkgSize = 0;
                    }
                }
            }

            private void RecvThread()
            {
                while (true)
                {
                    if (shouldKill)
                    {
                        try
                        {
                            Thread.CurrentThread.Abort();
                        }
                        catch (Exception e)
                        {
#if DebugMode
                            if (GameSettings.Instance.EnableSocketClosingException)
                            {
                                Debug.Log($"[Tcp.shouldKill.Abort]{e}");
                            }
#endif
                        }
                        break;
                    }
                    if (!this.clientSocket.Connected)
                    {
#if DebugMode
                        if(ifEnableSocketLog())
                            Debug.Log($"Tcp[{Ip}:{Port}] 连接断开，退出接收");
#endif
                        break;
                    }

                    try
                    {
                        var recvLen = 0;
                        if (this.usedBufferLength < MaxBufferSize)
                        {
                            recvLen = this.clientSocket.Receive(this.receiveBuffer,this.usedBufferLength, MaxBufferSize-this.usedBufferLength,SocketFlags.None);
                        }
                        else
                        {// 大包
                            Debug.LogWarning($"出现大包！");
                            if (this.longPkg == null)
                            {// 尚未分配内存
                                TcpProtocol.ReadHeader(this.receiveBuffer, this.usedBufferLength, out var pkgSize, out var headSize);
                                this.longPkgSize = pkgSize;
                                this.longPkg = new byte[longPkgSize];
                                Array.Copy(this.receiveBuffer, 0, this.longPkg, 0, this.usedBufferLength);
                            }

                            recvLen = this.clientSocket.Receive(this.longPkg, this.usedBufferLength, this.longPkgSize - this.usedBufferLength,
                                SocketFlags.None);
                        }

                        if (recvLen > 0)
                        {// 收到数据长度
                            this.usedBufferLength += recvLen;
                            OnRecvTcpData();
                        }
                    }
                    catch (Exception e)
                    {
                        if(this.onException!=null)
                            this.onException(e);
                        break;
                    }
                }
            }

            #endregion
    }
}


text:
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using UnityEngine;

namespace ReadyGamerOne.Data
{
    /// <summary>
    /// 可序列化字典
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    [System.Serializable]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable,
        ISerializationCallbackReceiver
    {

        #region 构造函数

        public SerializableDictionary()
            : base()
        {
        }

        public SerializableDictionary(IDictionary<TKey, TValue> dictionary)
            : base(dictionary)
        {
        }

        public SerializableDictionary(IEqualityComparer<TKey> comparer)
            : base(comparer)
        {
        }

        public SerializableDictionary(int capacity)
            : base(capacity)
        {
        }

        public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)
            : base(capacity, comparer)
        {
        }

        protected SerializableDictionary(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        #endregion

        #region  IXmlSerializable

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// 从对象的 XML 表示形式生成该对象
        /// </summary>
        /// <param name="reader"></param>
        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();
            if (wasEmpty)
                return;
            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                reader.ReadStartElement("item");
                reader.ReadStartElement("key");
                TKey key = (TKey)keySerializer.Deserialize(reader);
                reader.ReadEndElement();
                reader.ReadStartElement("value");
                TValue value = (TValue)valueSerializer.Deserialize(reader);
                reader.ReadEndElement();
                this.Add(key, value);
                reader.ReadEndElement();
                reader.MoveToContent();
            }

            reader.ReadEndElement();
        }

        /// <summary>
        /// 将对象转换为其 XML 表示形式
        /// </summary>
        /// <param name="writer"></param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            foreach (TKey key in this.Keys)
            {
                writer.WriteStartElement("item");
                writer.WriteStartElement("key");
                keySerializer.Serialize(writer, key);
                writer.WriteEndElement();
                writer.WriteStartElement("value");
                TValue value = this[key];
                valueSerializer.Serialize(writer, value);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
        }

        #endregion

        #region ISerializationCallbackReceiver

        [SerializeField] private List<TKey> _keys = new List<TKey>();
        [SerializeField] private List<TValue> _values = new List<TValue>();
        public void OnBeforeSerialize()
        {
            _keys.Clear();
            _values.Clear();
            _keys.Capacity = this.Count;
            _values.Capacity = this.Count;
            foreach (var kvp in this)
            {
                _keys.Add(kvp.Key);
                _values.Add(kvp.Value);
            }
        }

        public void OnAfterDeserialize()
        {
            this.Clear();
            int count = Mathf.Min(_keys.Count, _values.Count);
            for (int i = 0; i < count; ++i)
            {
                this.Add(_keys[i], _values[i]);

            }
        }

        #endregion
    }
}


text:
fileFormatVersion: 2
guid: 56d3bbf137f7adb439b3affb5a96c99b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
namespace ReadyGamerOne.Data
{
	public interface IStringInit
	{

		void Init(string args);
	}
}


text:
fileFormatVersion: 2
guid: 4562e9f0e01feab47aea36a76f542255
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
fileFormatVersion: 2
guid: 8aacea84b5a5e394b81222ac5575c6d1
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ReadyGamerOne.Global;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using DataConfigInfo=ReadyGamerOne.Data.CsvMgr.DataConfigInfo;
using FieldInfo=ReadyGamerOne.Data.CsvMgr.FieldInfo;

namespace ReadyGamerOne.Data
{
//#pragma warning disable CS0414

#if UNITY_EDITOR

    public class CsvDecodingTools:IEditorTools
    {
        private static string csvDirPath = "";

        private static GUIStyle titleStyle = new GUIStyle
        {
            fontSize = 12,
            alignment = TextAnchor.MiddleCenter
        };

        #pragma warning disable 414
        private static string Title = "CSV解析";
        #pragma warning restore 414
        private static string DataConfigFileName = "DataConfig";
        private static bool overriteOldFile = false;
        private static bool isPartial = true;
        private static Dictionary<string, DataConfigInfo> _dataConfigInfos = new Dictionary<string, DataConfigInfo>();
        private static void OnToolsGUI(string rootNs,string viewNs,string constNs,string dataNs,string autoDir,string scriptDir)
        {
            EditorGUILayout.Space();

            EditorGUILayout.HelpBox("使用本工具需要满足以下条件：\n" +
                                    "1、创建一个存放数据结构定义文件和数据文件的目录\n" +
                                    "2、创建数据结构问价 "+DataConfigFileName+" 定义数据类名，Fields，和父类\n" +
                                    "3、创建 \"类名.csv\" 文件定义类数据\n" +
                                    "4、将目录制定到文件目录，开始生成",MessageType.Info);

            var generateDir = Application.dataPath + "/" + rootNs + "/" + dataNs + "/" + autoDir;

            EditorGUILayout.LabelField("csv数据类生成目录", rootNs + "/" + dataNs + "/" + autoDir);

            EditorGUILayout.Space();
            GUILayout.Label("请选择Csv文件所在目录",titleStyle);

            if(Directory.Exists(csvDirPath) )
                GUILayout.Label(csvDirPath);
            else
            {
                EditorGUILayout.HelpBox("请选择CSV目录路径", MessageType.Warning);
            }
            if (GUILayout.Button("设置CSV数据文件所在目录"))
                csvDirPath = EditorUtility.OpenFolderPanel("选择Csv文件所在目录", Directory.GetParent(Application.dataPath).FullName,"");

            EditorGUILayout.Space();
            overriteOldFile = EditorGUILayout.Toggle("是否覆盖旧文件", overriteOldFile);
            EditorGUILayout.Space();
            isPartial = EditorGUILayout.Toggle("数据类型是否Partial", isPartial);
            EditorGUILayout.Space();

            if (GUILayout.Button("根据DataConfig文件生成空csv文件"))
            {
                if(!UnityEngine.Windows.Directory.Exists(csvDirPath))
                {
                    Debug.LogError("生成失败——请正确设置所有路径");
                    return;
                }

                if (!CreateMoreCsvFile())
                {
                    return;
                }
                AssetDatabase.Refresh();
                Debug.Log("生成完毕");
            }

            if (GUILayout.Button("生成C#协议文件",GUILayout.Height(3*EditorGUIUtility.singleLineHeight)))
            {
                if (!ReadDataConfigFile(true))
                {
                    Debug.Log("读取" + DataConfigFileName + "文件失败");
                    return;
                }
                var consDir = Application.dataPath + "/" + rootNs + "/" + constNs + "/" + autoDir;
                if (!Directory.Exists(consDir))
                    Directory.CreateDirectory(consDir);
                if (!Directory.Exists(generateDir))
                    Directory.CreateDirectory(generateDir);

                if (Directory.Exists(csvDirPath))
                {
                    foreach (var fileFullPath in Directory.GetFiles(csvDirPath))
                    {
                        if(fileFullPath.EndsWith(".meta"))
                            continue;
                        if(Path.GetFileNameWithoutExtension(fileFullPath)==DataConfigFileName)
                            continue;
                        if (fileFullPath.EndsWith(".csv") || fileFullPath.EndsWith(".CSV"))
                        {
                            CreatConfigFile(fileFullPath, generateDir,rootNs+"."+dataNs);
                        }
                    }

                    Utility.FileUtil.ReCreateFileNameConstClassFromDir(
                        "FileName",
                        consDir,
                        Application.dataPath + "/Resources/ClassFile",rootNs+"."+constNs);
                    AssetDatabase.Refresh();        //这里是一个点
                    Debug.Log("生成完成");
                }
                else
                {
                    Debug.LogError("生成失败——请正确设置所有路径");
                }
            }
        }

        /// <summary>
        /// 读取数据结构表信息
        /// </summary>
        /// <param name="copyToResource"></param>
        /// <returns></returns>
        private static bool ReadDataConfigFile(bool copyToResource=false)
        {
            var filePath = "";
            if (copyToResource)
            {
                var targetPath = Application.dataPath + "/Resources/ClassFile";

                if (!Directory.Exists(targetPath))
                    Directory.CreateDirectory(targetPath);
                if (File.Exists(csvDirPath + "/" + DataConfigFileName + ".CSV"))
                {
                    filePath = csvDirPath + "/" + DataConfigFileName + ".CSV";
                }else if (File.Exists(csvDirPath + "/" + DataConfigFileName + ".csv"))
                {
                    filePath = csvDirPath + "/" + DataConfigFileName + ".csv";
                }
                else
                {
                    return false;
                }

                var fileName = Path.GetFileNameWithoutExtension(filePath);

                targetPath = targetPath + "/" + fileName + ".csv";
                if (new FileInfo(targetPath).FullName != new FileInfo(filePath).FullName)
                {
                    File.Copy(filePath, targetPath, true);
                }
            }

            _dataConfigInfos.Clear();

            var csr = new CsvStreamReader(filePath);

            for (var i = 1; i < csr.RowCount + 1; i += 2)
            {
                var config = new DataConfigInfo();
                for (var j = 1; j < csr.ColCount + 1; j++)
                {
                    var name = csr[i+0, j];
                    var type = csr[i+1, j];

                    switch (type)
                    {
                        case "className":
                            config.className = name;
                            break;
                        case "parentClassName":
                            config.parentName = name;
                            break;
                        case "fileKeys":
                            foreach (var fileKey in name.Split('_'))
                            {
                                config.fileKeys.Add(fileKey.Trim());
                            }
                            break;
                        default:
                            if (string.IsNullOrEmpty(name))
                                break;
                            config.fieldInfos.Add(
                                name,
                                new FieldInfo
                            {
                                fieldName = name,
                                fieldType = type
                            });
                            break;
                    }
                }

                if (string.IsNullOrEmpty(config.className))
                {
                    return false;
                }

                if (config.fileKeys.Count == 0)
                {
                    config.fileKeys.Add(config.className);
                }

                _dataConfigInfos.Add(config.className, config);
            }

            return true;
        }

        /// <summary>
        /// 根据DataConfig文件生成空csv文件
        /// </summary>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private static bool CreateMoreCsvFile()
        {
            if(!ReadDataConfigFile(true))
            {
                Debug.Log("读取DataConfig文件失败");
                return false;
            }

            foreach (var kv  in _dataConfigInfos)
            {
                var curClassName = kv.Key;

                var classes = new Stack<string>();

                #region 将当前类所有祖先类都以此压入classes中

                classes.Push(curClassName);
                var temp = curClassName;
                while (!string.IsNullOrEmpty(temp))
                {
                    if (!_dataConfigInfos.ContainsKey(temp))
                    {
                        throw new Exception($"{DataConfigFileName}文件中未注册的类型：{temp}");
                    }
                    temp = _dataConfigInfos[temp].parentName;
                    if(!string.IsNullOrEmpty(temp))
                        classes.Push(temp);
                }

                #endregion

                var nameList=new List<string>();
                var typeList=new List<string>();

                #region 将当前类及其所有祖先类的所有字段和类型加入到nameList和typeList中去

                while (classes.Count > 0)
                {
                    var name = classes.Pop();
                    var config = _dataConfigInfos[name];
                    foreach (var _kv in config.fieldInfos)
                    {
                        nameList.Add(_kv.Value.fieldName);
                        typeList.Add(_kv.Value.fieldType);
                    }
                }

                #endregion

                Assert.IsTrue(kv.Value.fileKeys.Count>0);
                foreach (var fileKey in kv.Value.fileKeys)
                {
                    #region 生成文件：typeName_fileKey

                    var fileName = curClassName + "_" + fileKey;

                    if (File.Exists(csvDirPath + "/" + fileName + ".csv"))
                    {
                        if(overriteOldFile)
                            File.Delete(csvDirPath + "/" + fileName + ".csv");
                        else
                        {
                            continue;
                        }
                    }

                    var stream = new StreamWriter(csvDirPath + "/" + fileName + ".csv");

                    for (var i = 0; i < nameList.Count; i++)
                    {
                        if(i==nameList.Count-1)
                            stream.Write(nameList[i]);
                        else
                            stream.Write(nameList[i]+",");
                    }

                    stream.Write("\n");
                    for (var i = 0; i < typeList.Count; i++)
                    {
                        if(i==typeList.Count-1)
                            stream.Write(typeList[i]);
                        else
                            stream.Write(typeList[i]+",");
                    }

                    stream.Dispose();
                    stream.Close();

                    #endregion
                }

            }

            return true;
        }

        private static string GetIdName(string typeName)
        {
            Assert.IsTrue(_dataConfigInfos.ContainsKey(typeName));
            var info = _dataConfigInfos[typeName];
            while (!string.IsNullOrEmpty(info.parentName)
                   && _dataConfigInfos.ContainsKey(info.parentName))
            {
                info = _dataConfigInfos[info.parentName];
            }

            return info.fieldInfos.First().Value.fieldName;
        }

        /// <summary>
        /// 创建对应数据结构的类型定义
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="writePath"></param>
        /// <param name="nameSpace"></param>
        private static void CreatConfigFile(string filePath, string writePath,string nameSpace)
        {
            var targetPath = Application.dataPath + "/Resources/ClassFile";

            if (!Directory.Exists(targetPath))
                Directory.CreateDirectory(targetPath);

            string className = null;
            string fileKey = null;

            #region 获取className,fileKey并设置好targetPath

            {
                var fileName = Path.GetFileNameWithoutExtension(filePath);
                var strs = fileName.Split('_');

                if (strs.Length != 2)
                {
                    throw new Exception($"异常文件名：{fileName}");
                }
                className = strs[0].Trim();
                fileKey = strs[1].Trim();
                targetPath += "/" + fileName + ".csv";
            }

            #endregion

            if (new FileInfo(targetPath).FullName != new FileInfo(filePath).FullName)
            {
                File.Copy(filePath, targetPath, true);
            }

            var sw = new StreamWriter(writePath + "/" + className + ".cs");

            var parentClass = _dataConfigInfos[className].parentName;

            if (string.IsNullOrEmpty(parentClass))
            {
                sw.Write("using ReadyGamerOne.Data;\n");
                parentClass = "CsvMgr";
            }

            sw.WriteLine("using UnityEngine;\nusing System.Collections;\n");
            var ns = string.IsNullOrEmpty(nameSpace) ? "DefaultNamespace" : nameSpace;

            var partialType = isPartial ? " partial " : "";
            sw.WriteLine($"namespace {ns}\n" +
                         "{\n\t[System.Serializable]\n\tpublic "+ partialType + "class " + className + " : "+ parentClass);
            sw.WriteLine("\t{");

            var csr = new CsvStreamReader(filePath);

            sw.WriteLine("\t\tpublic const int "+className+"Count = "+(csr.RowCount-2)+";\n");

            var index = 0;
            foreach (var kv in _dataConfigInfos[className].fieldInfos)
            {
                var fieldName = kv.Value.fieldName;
                var fieldType = kv.Value.fieldType;

                if(index==0)
                    sw.WriteLine("\t\tpublic override string ID => "+GetIdName(className)+".ToString();\n");

                sw.WriteLine("\t\t" + "public " + fieldType + " " + fieldName + ";" + "");
                index++;
            }

            var toStringFunc = "\t\tpublic override string ToString()\n" +
                               "\t\t{\n" +
                               "\t\t\tvar ans=\"==《\t"+className+"\t》==\\n\" +\n";

            for (int colNum = 1; colNum < csr.ColCount + 1; colNum++)
            {
                string fieldName = csr[1, colNum];
                string fieldType = csr[2, colNum];

                toStringFunc += "\t\t\t\t\t" + "\"" + fieldName + "\" + \"\t\" + " + fieldName + "+\"\\n\" +\n";
            }

            toStringFunc = toStringFunc.Substring(0, toStringFunc.Length - "+\"\\n\" +\n".Length);

            toStringFunc += ";\n" +
                            "\t\t\treturn ans;\n" +
                            "\n\t\t}\n";

            sw.WriteLine(toStringFunc);

            sw.WriteLine("\t}\n" +
                         "}\n");

            sw.Flush();
            sw.Close();
        }
    }
#endif

}


text:
fileFormatVersion: 2
guid: 369124c72dd744d09d02c8a634ee7d2e
timeCreated: 1576121631


text:
fileFormatVersion: 2
guid: c3c4a9ecd3e08254289fa0af1327a067
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using ReadyGamerOne.Common;

namespace ReadyGamerOne.Data
{
	public class DataFactory<DataType>:Singleton<DataFactory<DataType>>,IDataFactory
		where DataType:class,ITxtSerializable,new()
	{
		public ITxtSerializable CreateData(string initLine)
		{
			var data = new DataType();
			data.SignInit(initLine);
			return data;
		}
	}

}


text:
namespace ReadyGamerOne.Data
{

    /// <summary>
    /// 需要Json命名空间支持 42行
    /// </summary>
    public static class JsonManager
    {
        //public static T LoadData<T>(string fullPath) where T : class
        //{
        //    StreamReader r = File.OpenText(fullPath);//_FileLocation是unity3D当前project的路径名，_FileName是xml的文件名。定义为成员变量了
        //                                             //当然，你也可以在前面先判断下要读取的xml文件是否存在
        //    String _data = r.ReadLine();
        //    //			Debug.Log(_data);
        //    var myData = DeserializeObject<T>(Encoding.UTF8.GetBytes(_data));//myData是上面自定义的xml存取过程中要使用的数据结构UserData
        //    r.Close();
        //    return myData;
        //}

        //public static void SaveData<T>(T data, string fullPath) where T : class
        //{
        //    StreamWriter writer;
        //    FileInfo t = new FileInfo(fullPath);
        //    t.Delete();
        //    writer = t.CreateText();
        //    var _data = SerializeObject(data); //序列化这组数据
        //    writer.WriteLine(Encoding.UTF8.GetString(_data)); //写入xml
        //                                                      //			writer.WriteLine(_data); //写入xml
        //    writer.Close();
        //}

        //public static byte[] SerializeObject<T>(T pObject) where T : class
        //{
        //    var obj = default(T);
        //    MemoryStream memoryStream = new MemoryStream();
        //    DataContractJsonSerializer djs = new DataContractJsonSerializer(typeof(T));
        //    djs.WriteObject(memoryStream, pObject);
        //    byte[] data = memoryStream.ToArray();
        //    memoryStream.Close();
        //    return data;
        //}

        //// Here we deserialize it back into its original form
        //public static T DeserializeObject<T>(byte[] aSerializeData) where T : class
        //{
        //    MemoryStream stream = new MemoryStream(aSerializeData);
        //    var obj = default(T);
        //    DataContractJsonSerializer djs = new DataContractJsonSerializer(typeof(T));

        //    obj = (T)djs.ReadObject(stream);
        //    stream.Close();

        //    return obj;
        //}
    }
}


text:
fileFormatVersion: 2
guid: 1f422d66e45361e458a09cd98d04e200
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:


text:
using System;
using System.Collections.Generic;
using System.Linq;
using ReadyGamerOne.Common;
using ReadyGamerOne.MemorySystem;
using UnityEngine;
using UnityEngine.Assertions;
using Random = UnityEngine.Random;

namespace ReadyGamerOne.Data
{
	public abstract class CsvMgr:IHasStringId
	{
		public class FieldInfo
		{
			public string fieldName;
			public string fieldType;
		}
		public class DataConfigInfo
		{
			public string className;
			public string parentName;
			public Dictionary<string,FieldInfo> fieldInfos=new Dictionary<string,FieldInfo>();
			public List<string> childrenNames=new List<string>();
			public List<string> fileKeys=new List<string>();
		}

		#region Private

		private static Dictionary<string, DataConfigInfo> _dataConfigInfos;

		/// <summary>
		/// 数据字典 Type => FileKey => data
		/// </summary>
		private static Dictionary<string,
			Dictionary<string,Dictionary<string,CsvMgr>>> allDataDic=new Dictionary<string, Dictionary<string, Dictionary<string, CsvMgr>>>();

		/// <summary>
		/// 安全插入数据字典
		/// </summary>
		/// <param name="typeName"></param>
		/// <param name="dataDic"></param>
		/// <param name="fileKey"></param>
		private static void SafeInsertDataDic(string typeName, Dictionary<string, CsvMgr> dataDic,
			string fileKey)
		{
			Assert.IsNotNull(dataDic);
			Assert.IsFalse(string.IsNullOrEmpty(typeName));
			Assert.IsFalse(string.IsNullOrEmpty(fileKey));
			Assert.IsTrue(_dataConfigInfos.ContainsKey(typeName));

			if (!allDataDic.ContainsKey(typeName))
			{
				allDataDic.Add(typeName, new Dictionary<string, Dictionary<string, CsvMgr>>());
			}

			var fileKeyDic = allDataDic[typeName];
			if (!fileKeyDic.ContainsKey(fileKey))
			{
				fileKeyDic.Add(fileKey, new Dictionary<string, CsvMgr>());
			}

			var targetDataDic = fileKeyDic[fileKey];
			foreach (var data in dataDic)
			{
				if (targetDataDic.ContainsKey(data.Key))
				{
					Debug.LogError($"{typeName}:{fileKey}重复ID：{data.Key}");
					continue;
				}

				targetDataDic.Add(data.Key, data.Value);
			}
		}

		/// <summary>
		/// 不考虑继承，安全查询一条数据，fileName为Null默认查询所有字典,fileName无效会报警告并查询所有字典
		/// </summary>
		/// <param name="dataId"></param>
		/// <param name="typeName"></param>
		/// <param name="fileName"></param>
		/// <returns></returns>
		private static CsvMgr SearchDataInternal(string dataId, Type type, string fileName)
		{
			Assert.IsFalse(string.IsNullOrEmpty(dataId));
			Assert.IsNotNull(type);
			Assert.IsTrue(_dataConfigInfos.ContainsKey(type.Name));

			var typeName = type.Name;

			//typeName库中有没有:
			if (!allDataDic.ContainsKey(typeName))
			{// 没有type就加载
				foreach (var fileKey in _dataConfigInfos[typeName].fileKeys)
				{
					// Debug.Log($"type:{typeName}, fileKey:{fileKey}");
					LoadConfigData(type,typeName+"_"+fileKey);
				}
			}

			// Debug.Log(2);
			Assert.IsTrue(allDataDic.ContainsKey(typeName));

			var fileDics = allDataDic[typeName];
			var warning = false;
			var searchall = false;
			if (string.IsNullOrEmpty(fileName))
			{
				searchall = true;
			}else if (!fileDics.ContainsKey(fileName))
			{
				searchall = true;
				warning = true;
			}

			if (searchall)
			{// 寻找所有dataDic
				foreach (var fileKey_dataDic in fileDics)
				{
					// Debug.Log($"try search {fileKey_dataDic.Key}, dataId: {dataId}");
					var dataDic = fileKey_dataDic.Value;
					if (dataDic.Count == 0)
					{
						if (warning)
						{
							Debug.LogWarning($"dataDic is empty: TypeName{typeName}, ItemId{dataId}, FileName{fileName}，已查询所有字典代替");
						}
					}
					else if (dataDic.ContainsKey(dataId))
					{
						if (warning)
						{
							Debug.LogWarning($"无效FileKey:{fileName}，已查询所有字典代替");
						}

						return dataDic[dataId];
					}
				}
				Debug.Log($"Failed from search all, dataId:{dataId}");
			}
			else
			{
				//寻找指定FileKeyDic
				var dataDic = fileDics[fileName];
				if (dataDic.ContainsKey(dataId))
				{
					return dataDic[dataId];
				}
			}

			Debug.LogError($"???,type:{type.Name},fileName:{fileName}");
			return null;
		}

		/// <summary>
		/// 不考虑继承，安全查询所有typename的数据，fileName为Null默认查询所有字典,fileName无效会报警告并查询所有字典
		/// </summary>
		/// <param name="typeName"></param>
		/// <param name="fileName"></param>
		/// <returns></returns>
		private static Dictionary<string, CsvMgr> GetDatasInternal(Type dataType, string fileName)
		{
			Assert.IsNotNull(dataType);
			Assert.IsTrue(_dataConfigInfos.ContainsKey(dataType.Name));

			var typeName = dataType.Name;

			if (!allDataDic.ContainsKey(dataType.Name))
			{
				foreach (var fileKey in _dataConfigInfos[typeName].fileKeys)
				{
					LoadConfigData(dataType,typeName+"_"+fileKey);
				}
			}

			Assert.IsTrue(allDataDic.ContainsKey(dataType.Name));

			var ans = new Dictionary<string,CsvMgr>();

			var fileDics = allDataDic[dataType.Name];
			var searchall = false;
			if (string.IsNullOrEmpty(fileName))
			{
				searchall = true;
			}else if (!fileDics.ContainsKey(fileName))
			{
				searchall = true;
				Debug.LogWarning($"无效FileKey:{fileName}，已查询所有字典代替");
			}

			if (searchall)
			{
				foreach (var fileKey_dataDic in fileDics)
				{
					foreach (var id_data in fileKey_dataDic.Value)
					{
						if (id_data.Value != null)
						{
							ans.Add(id_data.Key, id_data.Value);
						}
					}
				}
			}
			else
			{
				foreach (var id_data in fileDics[fileName])
				{
					if (id_data.Value != null)
					{
						ans.Add(id_data.Key, id_data.Value);
					}
				}
			}

			return ans;
		}

		/// <summary>
		/// 根据typeName获取本身及其子类所有需要加载的
		/// </summary>
		/// <param name="typeName"></param>
		/// <returns></returns>
		/// <exception cref="Exception"></exception>
		private static List<string> GetAllFileNameWithTypeName(string typeName)
		{
			Assert.IsFalse(string.IsNullOrEmpty(typeName));

			if(null==_dataConfigInfos)
				LoadRootConfigData();
			if(!_dataConfigInfos.ContainsKey(typeName))
				throw new Exception($"未注册的类型:{typeName}");
			var ans=new List<string>();
			//添加自身
			foreach (var fileKey in _dataConfigInfos[typeName].fileKeys)
			{
				ans.Add(typeName+"_"+fileKey);
			}
			//添加子类
			foreach (var typeName_info in _dataConfigInfos)
			{
				if (typeName_info.Value.parentName == typeName)
				{
					ans.AddRange(GetAllFileNameWithTypeName(typeName_info.Key));
				}
			}

			return ans;
		}

		/// <summary>
		/// 加载数据，fileKey不合法的时候默认取type.Name
		/// </summary>
		/// <param name="type"></param>
		/// <param name="filePath"></param>
		private static void LoadConfigData(Type type, string fileName)
		{
			Assert.IsNotNull(type);
			Assert.IsNotNull(_dataConfigInfos);
			Assert.IsFalse(string.IsNullOrEmpty(fileName));

			CsvReaderByString csr = null;

			#region 加载csr并初始化fileKey

			//fileKey不合法的时候默认取type.Name

			var asset = ResourceMgr.GetAsset<TextAsset>(fileName, OriginBundleKey.File);

			if(asset==null)
				throw new Exception($"加载数据文件失败,typeKey:{type.Name},fileName:{fileName}");

			csr = new CsvReaderByString (asset.text);

			#endregion

			System.Reflection.FieldInfo[] fieldInfos = null;

			#region 根据type初始化fieldInfos

			fieldInfos = new System.Reflection.FieldInfo[csr.ColCount];
			for (var colNum=1; colNum<csr.ColCount+1; colNum++) {
				var info = type.GetField (csr [1, colNum]);

				if (info == null)
				{
					throw new Exception($"GetField failed:如果使用了多态，请尽量使用具体类型代替\ntype:{type.Name}\nfileKey:{fileName}\nfieldName:{csr[1,colNum]}");
				}

				fieldInfos[colNum - 1] = info;
			}

			#endregion

			var dataRow = new Dictionary<string, CsvMgr> ();

			//每行都是一条数据，所以循环行数次
			for (var i=2; i<csr.RowCount+1; i++) {

				var dataObj = Activator.CreateInstance(type);

				//逐个判断当前行每一列域类型加入字典
				for (var j=0; j<fieldInfos.Length; j++) {
					var fieldValue = csr [i, j + 1];

					//试探可能的类型，将数据填充到数据项dataItem中
					var dataItem = new object ();
					try
					{
						switch (fieldInfos [j].FieldType.ToString ()) {
							case "System.Single":
								dataItem = string.IsNullOrEmpty(fieldValue) ? default(float) : float.Parse(fieldValue);
								break;
							case "System.Int32":
								dataItem = string.IsNullOrEmpty(fieldValue) ? default(int) : int.Parse (fieldValue);
								break;
							case "System.Int64":
								dataItem = string.IsNullOrEmpty(fieldValue) ? default(long) : long.Parse (fieldValue);
								break;
							case "System.String":
								dataItem = string.IsNullOrEmpty(fieldValue) ? default(string) : fieldValue;
								break;
							default:
								throw new Exception($"意料之外的字段类型:{fieldInfos [j].FieldType}\ntype:{type.Name}\nfileKey:{fileName}");
						}
					}
					catch (FormatException e)
					{
						throw new Exception($"{e.Message}\ninputString:{fieldValue}\ntype:{type.Name}\nfileKey:{fileName}\npos:[{i},{j}]");
					}

					fieldInfos [j].SetValue (dataObj, dataItem);

					//如果是第一列，这一列被视为主键
					if (j==0) {
//						Debug.Log(dataObj);
						dataRow.Add (dataItem.ToString (), (CsvMgr)dataObj);
					}
				}

				// Debug.Log($"$insert data {dataObj}");
			}

			if (dataRow.Count == 0)
			{
				Debug.LogWarning($"itemDataRow is empty: TypeName{type.Name}, fileName:{fileName}");
			}

			//debug
			// foreach (var kv in dataRow)
			// {
			// 	Debug.Log($"success get data: {kv.Value}");
			// }

			SafeInsertDataDic(
				type.Name,
				dataRow,
				fileName);
		}

		/// <summary>
		/// 读取数据结构信息
		/// </summary>
		private static void LoadRootConfigData()
		{
			//避免重复初始化
			if (_dataConfigInfos != null)
				return;

			_dataConfigInfos=new Dictionary<string, DataConfigInfo>();
			var getString = ResourceMgr.GetAsset<TextAsset>("DataConfig",OriginBundleKey.File).text;

			var csr = new CsvReaderByString (getString);
			for (var i = 1; i < csr.RowCount + 1; i += 2)
			{
				var config = new DataConfigInfo();
				for (var j = 1; j < csr.ColCount + 1; j++)
				{
					var name = csr[i+0, j];
					var type = csr[i+1, j];
					switch (type)
					{
						case "className":
							config.className = name;
							break;
						case "parentClassName":
							if (!string.IsNullOrEmpty(name))
							{
								config.parentName = name;
								_dataConfigInfos[config.parentName]
									.childrenNames.Add(config.className);
							}
							break;
						case "fileKeys":
							foreach (var fileKey in name.Split('_'))
							{
								config.fileKeys.Add(fileKey.Trim());
							}
							break;
						default:
							if (string.IsNullOrEmpty(name))
								break;
							config.fieldInfos.Add(
								name,
								new FieldInfo
								{
									fieldName = name,
									fieldType = type
								});
							break;
					}
				}
				if (string.IsNullOrEmpty(config.className))
				{
					throw new Exception("ClassName is null ???");
				}
				if (config.fileKeys.Count == 0)
				{
					config.fileKeys.Add(config.className);
				}

				_dataConfigInfos.Add(config.className, config);
			}
		}

		#endregion

		/// <summary>
		/// 考虑继承，安全查询数据，fileKey为Null默认查询所有字典,fileKey无效会报警告并查询所有字典
		/// </summary>
		/// <param name="dataId"></param>
		/// <param name="typeName"></param>
		/// <param name="fileName"></param>
		/// <returns></returns>
		public static CsvMgr GetData(string dataId, Type dataType, string fileName = null)
		{
			dataId = dataId.Trim();

			if(_dataConfigInfos==null)
				LoadRootConfigData();

			Assert.IsFalse(string.IsNullOrEmpty(dataId));
			Assert.IsNotNull(dataType);

			//如果数据结构表中没有，那么必然没有
			if (!_dataConfigInfos.ContainsKey(dataType.Name))
			{
				Debug.LogWarning($"未注册的数据类型：{dataType.Name}");
				return null;
			}

			// Debug.Log(1);
			var ans = SearchDataInternal(dataId, dataType, fileName);
			if (ans != null)
				return ans;

			//查找子类型
			var nameSpace = dataType.Namespace;
			foreach (var typeName_info in _dataConfigInfos)
			{
				var info = typeName_info.Value;
				if (info.parentName != dataType.Name)
					continue;

				var typeName = nameSpace + "." + info.className;
				var childType = Type.GetType(typeName);
				if (childType == null)
				{
					Debug.LogError($"错误类型:{typeName},dataId:{dataId}");
					continue;
				}
				if (!_dataConfigInfos.ContainsKey(childType.Name))
				{
					Debug.LogError($"未注册的类型：{typeName},dataId:{dataId}");
					continue;
				}

				ans = SearchDataInternal(dataId, childType, fileName);

				if (ans != null)
					return ans;
			}

			throw new Exception($"遍历所有子类型也没有找到这个id:{dataId},typeName:{dataType.Name}");
		}

		/// <summary>
		/// 考虑继承，安全查询数据返回T，fileKey为Null默认查询所有字典,fileKey无效会报警告并查询所有字典
		/// </summary>
		/// <param name="dataId"></param>
		/// <param name="fileName"></param>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public static T GetData<T>(string dataId, string fileName = null)
			where T:CsvMgr
		{
			return GetData(dataId, typeof(T), fileName) as T;
		}

		/// <summary>
		/// 获取某个种类随机一个数据
		/// </summary>
		/// <param name="filePath"></param>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public static T GetRandomData<T>(string fileName = null)
			where T:CsvMgr
		{
			//如果没有读取数据结构表，就先读取
			if(_dataConfigInfos==null)
				LoadRootConfigData();

			var typeName = typeof(T).Name;

			//如果数据结构表中没有，那么必然没有
			if (!_dataConfigInfos.ContainsKey(typeName))
			{
				Debug.LogError($"未注册的数据类型：{typeName}");
				return null;
			}

			//如果数据库中没有加载，就加载
			if (!allDataDic.ContainsKey(typeName))
			{
				foreach (var fileKey in _dataConfigInfos[typeName].fileKeys)
				{
					LoadConfigData(typeof(T),typeName+"_"+fileKey);
				}
			}

			var fileDic = allDataDic[typeName];

			var all = 0;
			var counts = new List<Dictionary<string,CsvMgr>>();

			if (fileName==null)
			{//	遍历本类型所有
				foreach (var fileKey_dataDic in fileDic)
				{
					all += fileKey_dataDic.Value.Count;
					counts.Add(fileKey_dataDic.Value);
				}
			}
			else if (string.IsNullOrEmpty(fileName))
			{// fileName=""
				Debug.LogWarning("fileName is empty, has search all");
				foreach (var fileKey_dataDic in fileDic)
				{
					all += fileKey_dataDic.Value.Count;
					counts.Add(fileKey_dataDic.Value);
				}
			}
			else
			{
				if (!fileDic.ContainsKey(fileName))
				{
					Debug.Log("Load from random");
					LoadConfigData(typeof(T),fileName);

					if(!fileDic.ContainsKey(fileName))
						throw new Exception($"Type:{typeName} don't have this fileName:{fileName}");
				}

				all += fileDic[fileName].Count;
				counts.Add(fileDic[fileName]);
			}

			var random = Random.Range(0, all);
			var temp = 0;
			foreach (var dataDic in counts)
			{
				if (random >= temp && random < temp + dataDic.Count)
				{//	找到目标
					return dataDic.Values.ToArray()[random-temp] as T;
				}

				temp += dataDic.Count;
			}

			throw new Exception("查找越界");
		}

		/// <summary>
		/// 根据typeName获取所有类型数据，如果fileKey为null，就查询所有，fileKey无效会警告
		/// </summary>
		/// <param name="filePath"></param>
		/// <typeparam name="T"></typeparam>
		public static Dictionary<string,CsvMgr> GetDatas(Type dataType, string fileKey = null)
		{
			if(_dataConfigInfos==null)
				LoadRootConfigData();

			var ans = new Dictionary<string, CsvMgr>();

			if (!_dataConfigInfos.ContainsKey(dataType.Name))
				return ans;

			//加入当前类型
			var tempAns = GetDatasInternal(dataType, fileKey);
			if (tempAns != null)
			{
				foreach (var id_data in tempAns)
				{
					ans.Add(id_data.Key, id_data.Value);
				}
			}

			//加入子类型
			var nameSpace = dataType.Namespace;
			foreach (var typeName_info in _dataConfigInfos)
			{
				var info = typeName_info.Value;
				if (info.parentName == dataType.Name)
				{
					var childType = Type.GetType(nameSpace + info.className);
					if (childType == null)
					{
						Debug.LogError("错误类型:"+nameSpace + info.className);
						continue;
					}
					if (!_dataConfigInfos.ContainsKey(childType.Name))
					{
						Debug.LogError("未注册的类型："+nameSpace + info.className);
						continue;
					}

					//加入子类型
					tempAns = GetDatasInternal(childType, fileKey);
					if (tempAns != null)
					{
						foreach (var id_data in tempAns)
						{
							ans.Add(id_data.Key, id_data.Value);
						}
					}

				}
			}

			return ans;
		}

		/// <summary>
		/// 根据T获取所有类型数据，如果fileKey为null，就查询所有，fileKey无效会警告
		/// </summary>
		/// <param name="filePath"></param>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public static Dictionary<string,T> GetDatas<T> (string fileKey = null)
			where T:CsvMgr
		{
			var tempAns = GetDatas(typeof(T), fileKey);

			var ans = new Dictionary<string, T>();
			foreach (var id_data in tempAns)
			{
				var data = id_data.Value as T;
				if (data != null)
					ans.Add(id_data.Key, data);
			}

			return ans;
		}

		public abstract string ID { get; }
	}
}