From 716328741ce6d4fca4fd1fa96b38906cd2d6b4a8 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:39:19 +0800 Subject: [PATCH] Docs Stash --- src/Debug/DragonDocs/DragonDocs.cs | 289 ++++++++++++++++ src/Debug/DragonDocs/DragonDocsMeta.cs | 214 ++++++++++++ src/Debug/DragonDocs/JsonIterator.cs | 215 ++++++++++++ src/Debug/DragonDocs/JsonStreamReader.cs | 14 + src/Debug/EcsDebugUtility.cs | 323 +---------------- .../MetaAttributes/MetaColorAttribute.cs | 57 ++- src/Debug/TypeMeta.cs | 326 ++++++++++++++++++ src/EcsRunner.cs | 3 +- src/EcsWorld.cs | 2 +- src/Pools/EcsPool.cs | 2 +- src/Pools/EcsPoolBase.cs | 2 +- src/Pools/EcsTagPool.cs | 2 +- 12 files changed, 1124 insertions(+), 325 deletions(-) create mode 100644 src/Debug/DragonDocs/DragonDocs.cs create mode 100644 src/Debug/DragonDocs/DragonDocsMeta.cs create mode 100644 src/Debug/DragonDocs/JsonIterator.cs create mode 100644 src/Debug/DragonDocs/JsonStreamReader.cs create mode 100644 src/Debug/TypeMeta.cs diff --git a/src/Debug/DragonDocs/DragonDocs.cs b/src/Debug/DragonDocs/DragonDocs.cs new file mode 100644 index 0000000..4fdf3c6 --- /dev/null +++ b/src/Debug/DragonDocs/DragonDocs.cs @@ -0,0 +1,289 @@ +using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.PoolsCore; +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace DCFApixels.DragonECS.Docs +{ + [Serializable] + [DataContract] + public class DragonDocs : ISerializable + { + [NonSerialized] + private List _metas = new List(); + [DataMember] + private SortedDictionary> _groups = new SortedDictionary>(); + + protected DragonDocs(SerializationInfo info, StreamingContext context) + { + + } + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new NotImplementedException(); + } + private DragonDocs() { } + private DragonDocs(List metaList) + { + _metas = metaList; + foreach (var docsMeta in _metas) + { + string group = docsMeta.Group; + if (_groups.TryGetValue(group, out List groupMetas) == false) + { + groupMetas = new List(); + _groups.Add(group, groupMetas); + } + groupMetas.Add(docsMeta); + } + } + + public static DragonDocs Generate() + { + DragonDocs result = new DragonDocs(); + List types = GetTypes(); + foreach (var type in types) + { + TypeMeta meta = type.ToMeta(); + DragonDocsMeta docsMeta = new DragonDocsMeta(meta); + result._metas.Add(docsMeta); + string group = meta.Group.Name; + + if (result._groups.TryGetValue(group, out List groupMetas) == false) + { + groupMetas = new List(); + result._groups.Add(group, groupMetas); + } + + groupMetas.Add(docsMeta); + } + + return result; + } + private static List GetTypes() + { + Type ecsMetaAttributeType = typeof(EcsMetaAttribute); + List result = new List(); + Type memberType = typeof(IEcsMember); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var types = assembly.GetTypes(); + foreach (var type in types) + { + if (memberType.IsAssignableFrom(type) || Attribute.GetCustomAttributes(type, ecsMetaAttributeType, false).Length > 1) + { + result.Add(type); + } + } + } + return result; + } + + + + #region Serialization + public string SerializeToJson() + { + StringBuilder json = new StringBuilder(); + json.Append("{ \"data\": [\r"); + foreach (var group in _groups) + { + //json.Append($"\"{group.Key}\": "); + json.Append($"{{ \"key\": \"{group.Key}\",\r\"values\": ["); + foreach (var meta in group.Value) + { + meta.SerializeToJson(json); + } + json.Append("] }, \r"); + } + json.Append(" ] }"); + return json.ToString(); + } + + + + private static DragonDocs CreateDocs(ref JsonIterator reader) + { + JsonNode node; + List metas = new List(); + while (reader.Next()) + { + node = reader.Next(); + if (node.IsKey && node.EqualsString("data") && reader.Next().IsEnter) + { + while (reader.Next().IsEnter && (node = reader.Next()).IsKey && node.EqualsString("key")) + { + node = reader.Next(); + string group = node.ToString(); + node = reader.Next(); + if (node.IsKey && node.EqualsString("values") && reader.Next().IsEnter) + { + while (reader.Next().IsEnter) + { + metas.Add(DragonDocsMeta.DeserializeFromJson(ref reader, group)); + } + } + if(reader.Next().IsExit == false) + { + throw new Exception(); + } + } + return new DragonDocs(metas); + } + else + { + throw new Exception(); + } + } + throw new Exception(); + } + public unsafe static DragonDocs DeserializeFromJson(string jsonString) + { + CB.Set(jsonString); + fixed (char* ptr = jsonString) + { + JsonIterator reader = new JsonIterator(ptr, jsonString.Length); + return CreateDocs(ref reader); + } + throw new Exception(); + + + + + //fixed (char* ptr = jsonString) + //{ + // //var result = DragonDocsJsonReader.ReadJson(ptr, jsonString.Length); + // + // + //} + // + //throw new NotImplementedException(); + + //jsonString = jsonString.Replace("{", "").Replace("}", "").Replace("\"", ""); + + // Разбиваем JSON строку на пары ключ-значение + //string[] parts = jsonString.Split(new string[] { "key:", "values:[" }, StringSplitOptions.RemoveEmptyEntries); + + + + //string[] parts = jsonString.Split("\"key\":", StringSplitOptions.RemoveEmptyEntries); + //List metas = new List(); + // + // + //for (int i = 1; i < parts.Length; i += 2) + //{ + // string key = parts[i]; + // int indexOf = key.IndexOf('"'); + // key = key.Substring(indexOf + 1, key.IndexOf('"', indexOf + 1) - indexOf - 1); + // string values = parts[i + 1]; + // + // Match match = Regex.Match(values, @"""values"": \[(.*?)\].*"); + // if (match.Success) + // { + // values = match.Groups[1].Value; + // } + // + // CB.Set(values); + // + // + // string[] metaValues = values.Split(new string[] { "},{" }, StringSplitOptions.None); + // + // foreach (string metaValue in metaValues) + // { + // // Десериализация отдельного метаданных (нужно реализовать метод DeserializeFromJson в DragonDocsMeta) + // DragonDocsMeta meta = DragonDocsMeta.DeserializeFromJson("{" + metaValue + "}", key); + // metas.Add(meta); + // } + //} + //return new DragonDocs(metas); + + + + + // Удаляем лишние символы из JSON строки + //json = json.Replace("{", "").Replace("}", "").Replace("\"", ""); + // + //// Разбиваем JSON строку на пары ключ-значение + //string[] pairs = json.Split(','); + // + //// Создаем переменные для хранения текущих значений + //string currentKey = ""; + //List currentMetas = new List(); + // + //foreach (string pair in pairs) + //{ + // string[] keyValue = pair.Split(':'); + // + // if (keyValue.Length == 2) + // { + // string key = keyValue[0].Trim(); + // string value = keyValue[1].Trim(); + // + // if (key == "key") + // { + // currentKey = value; + // } + // else if (key == "values") + // { + // // Очищаем список метаданных перед добавлением новых + // currentMetas.Clear(); + // + // // Разбиваем значение на отдельные метаданные и десериализуем каждое из них + // string[] metaValues = value.Split(new string[] { "},{" }, StringSplitOptions.None); + // foreach (string metaValue in metaValues) + // { + // // Десериализация отдельного метаданных (нужно реализовать метод DeserializeFromJson в DragonDocsMeta) + // DragonDocsMeta meta = DragonDocsMeta.DeserializeFromJson("{" + metaValue + "}", currentKey); + // currentMetas.Add(meta); + // } + // } + // } + //} + //return new DragonDocs(currentMetas); + } + + + + //public static DragonDocs DeserializeFromJson(string jsonString) + //{ + // //jsonString = jsonString.Replace("{", "").Replace("}", "").Replace("\"", ""); + // + // // Разбиваем JSON строку на пары ключ-значение + // //string[] parts = jsonString.Split(new string[] { "key:", "values:[" }, StringSplitOptions.RemoveEmptyEntries); + // string[] parts = jsonString.Split("\"key\":", StringSplitOptions.RemoveEmptyEntries); + // List metas = new List(); + // + // + // for (int i = 1; i < parts.Length; i += 2) + // { + // string key = parts[i]; + // int indexOf = key.IndexOf('"'); + // key = key.Substring(indexOf + 1, key.IndexOf('"', indexOf + 1) - indexOf - 1); + // string values = parts[i + 1]; + // + // Match match = Regex.Match(values, @"""values"": \[(.*?)\].*"); + // if (match.Success) + // { + // values = match.Groups[1].Value; + // } + // + // CB.Set(values); + // + // + // string[] metaValues = values.Split(new string[] { "},{" }, StringSplitOptions.None); + // + // foreach (string metaValue in metaValues) + // { + // // Десериализация отдельного метаданных (нужно реализовать метод DeserializeFromJson в DragonDocsMeta) + // DragonDocsMeta meta = DragonDocsMeta.DeserializeFromJson("{" + metaValue + "}", key); + // metas.Add(meta); + // } + // } + // return new DragonDocs(metas); + // + //} + #endregion + } +} \ No newline at end of file diff --git a/src/Debug/DragonDocs/DragonDocsMeta.cs b/src/Debug/DragonDocs/DragonDocsMeta.cs new file mode 100644 index 0000000..779c3f9 --- /dev/null +++ b/src/Debug/DragonDocs/DragonDocsMeta.cs @@ -0,0 +1,214 @@ +using DCFApixels.DragonECS.Internal; +using System; +using System.Text; + +namespace DCFApixels.DragonECS.Docs +{ + public class DragonDocsMeta + { + private Type _sourceType; + private bool _isInitSourceType = false; + public readonly string AssemblyQualifiedName = string.Empty; + + public readonly string Name = string.Empty; + public readonly bool IsCustomName = false; + public readonly MetaColor Color = MetaColor.BlackColor; + public readonly bool IsCustomColor = false; + public readonly string Autor = string.Empty; + public readonly string Description = string.Empty; + + public readonly string Group = string.Empty; + public readonly string Tags = string.Empty; + + public DragonDocsMeta(TypeMeta meta) + { + _sourceType = meta.Type; + AssemblyQualifiedName = meta.Type.AssemblyQualifiedName; + + Name = meta.Name; + IsCustomName = meta.IsCustomName; + Color = meta.Color; + IsCustomColor = meta.IsCustomColor; + Autor = meta.Description.Author; + Description = meta.Description.Text; + + Group = meta.Group.Name; + Tags = string.Join(", ", meta.Tags); + } + + public bool TryGetSourceType(out Type type) + { + type = GetSourceType(); + return type != null; + } + private Type GetSourceType() + { + if (_isInitSourceType) { return _sourceType; } + _isInitSourceType = true; + _sourceType = Type.GetType(AssemblyQualifiedName); + return _sourceType; + } + + #region Serialization + public void SerializeToJson(StringBuilder json) + { + json.Append("{"); + + SerializeFieldToJson(json, nameof(AssemblyQualifiedName), AssemblyQualifiedName, true, true); + SerializeFieldToJson(json, nameof(Name), Name, true, true); + SerializeFieldToJson(json, nameof(IsCustomName), IsCustomName.ToString().ToLower(), false, true); + SerializeFieldToJson(json, nameof(Color), Color.ToString(), true, true); + SerializeFieldToJson(json, nameof(IsCustomColor), IsCustomColor.ToString().ToLower(), false, true); + + SerializeFieldToJson(json, nameof(Autor), Autor, true, false); + SerializeFieldToJson(json, nameof(Description), Description, true, false); + //SerializeFieldToJson(json, nameof(Group), Group, false); + SerializeFieldToJson(json, nameof(Tags), Tags, true, false); + + json.Remove(json.Length - 1, 1); + json.Append("}"); + } + private void SerializeFieldToJson(StringBuilder json, string fieldName, string value, bool isString, bool alwaysSerialize) + { + if (alwaysSerialize || !string.IsNullOrEmpty(value)) + { + json.Append($"\"{fieldName}\":"); + if (isString) + { + json.Append($"\"{value}\""); + } + else + { + json.Append($"{value}"); + } + json.Append(","); + } + } + + + internal static DragonDocsMeta DeserializeFromJson(ref JsonIterator reader, string group) + { + return new DragonDocsMeta(ref reader, group); + } + private unsafe DragonDocsMeta(ref JsonIterator reader, string group) + { + Group = group; + JsonNode node; + int depth = reader.Depth; + while ((node = reader.Next()).Depth >= depth) + { + //TODO тут можно сделать систему которая бы учитывала что порядок полей может меняться по мере разработки. + //Есть идея через рефлекшн разбирать состояние типа, после создавать массив индексов, + //а эти индексы потом могли бы применяться в switch-е в котором кейсы на каждый индекс по порядку + if (node.IsKey && node.EqualsString(nameof(AssemblyQualifiedName))) + { + AssemblyQualifiedName = reader.Next().ToString(); + if ((node = reader.Next()).Depth < depth) { break; } + } + if (node.IsKey && node.EqualsString(nameof(Name))) + { + Name = reader.Next().ToString(); + if ((node = reader.Next()).Depth < depth) { break; } + } + if (node.IsKey && node.EqualsString(nameof(IsCustomName))) + { + IsCustomName = UnsafeBoolParse(reader.Next().Ptr); + if ((node = reader.Next()).Depth < depth) { break; } + } + if (node.IsKey && node.EqualsString(nameof(Color))) + { + node = reader.Next(); + Color = MetaColor.Parse(node.Ptr, node.Length); + if ((node = reader.Next()).Depth < depth) { break; } + } + if (node.IsKey && node.EqualsString(nameof(IsCustomColor))) + { + IsCustomColor = UnsafeBoolParse(reader.Next().Ptr); + if ((node = reader.Next()).Depth < depth) { break; } + } + + if (node.IsKey && node.EqualsString(nameof(Autor))) + { + Autor = reader.Next().ToString(); + if ((node = reader.Next()).Depth < depth) { break; } + } + if (node.IsKey && node.EqualsString(nameof(Description))) + { + Description = reader.Next().ToString(); + if ((node = reader.Next()).Depth < depth) { break; } + } + //if (node.IsKey && node.EqualsString(nameof(Group))) + //{ + // Group = reader.Next().ToString(); + // if ((node = reader.Next()).Depth < depth) { break; } + //} + if (node.IsKey && node.EqualsString(nameof(Tags))) + { + Tags = reader.Next().ToString(); + if ((node = reader.Next()).Depth < depth) { break; } + } + } + } + private static unsafe bool UnsafeBoolParse(char* ptr) + { + return char.ToLower(ptr[0]) == 't'; + } + + + + public static DragonDocsMeta DeserializeFromJson(string json, string currentGroup) + { + return new DragonDocsMeta(json, currentGroup); + } + /// DeserializeFromJson constructor. + private DragonDocsMeta(string json, string currentGroup) + { + // Удаляем лишние символы из JSON строки + json = json.Replace("{", "").Replace("}", "").Replace("\"", ""); + // Разбиваем JSON строку на пары ключ-значение + string[] pairs = json.Split(','); + + foreach (string pair in pairs) + { + string[] keyValue = pair.Split(':'); + + if (keyValue.Length == 2) + { + string key = keyValue[0].Trim(); + string value = keyValue[1].Trim(); + + switch (key) + { + case nameof(AssemblyQualifiedName): + AssemblyQualifiedName = value; + break; + case nameof(Name): + Name = value; + break; + case nameof(IsCustomName): + IsCustomName = bool.Parse(value); + break; + case nameof(Color): + Color = MetaColor.Parse(value); + break; + case nameof(IsCustomColor): + IsCustomColor = bool.Parse(value); + break; + case nameof(Autor): + Autor = value; + break; + case nameof(Description): + Description = value; + break; + case nameof(Tags): + Tags = value; + break; + default: break; + } + } + } + Group = currentGroup; + } + #endregion + } +} \ No newline at end of file diff --git a/src/Debug/DragonDocs/JsonIterator.cs b/src/Debug/DragonDocs/JsonIterator.cs new file mode 100644 index 0000000..2093ab6 --- /dev/null +++ b/src/Debug/DragonDocs/JsonIterator.cs @@ -0,0 +1,215 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS.Internal +{ + internal unsafe ref struct JsonIterator + { + private char* _jsonPtr; + private int _jsonLength; + private int _depth; + private JsonNode _currentNode; + private char _currentChar; + public int Depth + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _depth; } + } + public char Char + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _jsonPtr[0]; } + } + public JsonNode Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _currentNode; } + } + public JsonIterator(char* jsonPtr, int jsonLength) + { + _jsonPtr = jsonPtr - 1; + _jsonLength = jsonLength + 1; + _depth = 0; + _currentChar = '\0'; + _currentNode = default; + } + public JsonNode Next() + { + _currentNode = NextInternal(); + return _currentNode; + } + public override string ToString() { return new string(_jsonPtr, 0, _jsonLength); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private JsonNode NextInternal() + { + if (NextSymbol() == false) + { + return new JsonNode(JsonNodeType.NA, null, 0, _depth); ; + } + if (IsEnterChar(_currentChar)) + { + _depth++; + return new JsonNode(JsonNodeType.Enter, _jsonPtr, 1, _depth); + } + if (IsExitChar(_currentChar)) + { + _depth--; + return new JsonNode(JsonNodeType.Exit, _jsonPtr, 1, _depth); + } + if (_currentChar == '"') + { + return ReadString(); + } + if (char.IsLetter(_currentChar)) + { + return ReadValue(); + } + throw new FormatException("JSON format error."); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private JsonNode ReadString() + { + JsonSpan span = new JsonSpan(_jsonPtr); + bool isEscape = false; + while (Increment()) + { + if (_currentChar == '"' && isEscape == false) + { + JsonNodeType type = JsonNodeType.Value; + bool x = NextSymbol(); + if (x && _currentChar == ':') + { + type = JsonNodeType.Key; + } + else + { + Deincrement(); + } + return new JsonNode(type, span.ptr + 1, span.length, _depth); + } + isEscape = isEscape == false && _currentChar == '\\'; + span.length++; + } + throw new FormatException("JSON format error."); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private JsonNode ReadValue() + { + JsonSpan span = new JsonSpan(_jsonPtr); + while (Increment() && (char.IsWhiteSpace(_currentChar) || _currentChar == ',' || _currentChar == ':' || _currentChar == '}' || _currentChar == ']') == false) + { + span.length++; + } + JsonNodeType type = JsonNodeType.Value; + Deincrement(); + if (NextSymbol() && _currentChar == ':') + { + type = JsonNodeType.Key; + } + else + { + Deincrement(); + } + return new JsonNode(type, span.ptr, span.length + 1, _depth); + } + private ref struct JsonSpan + { + public char* ptr; + public int length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public JsonSpan(char* ptr) + { + this.ptr = ptr; + length = 0; + } + public override string ToString() { return new string(ptr, 0, length); } + } + private bool NextSymbol() + { + bool isNotEnd; + while ((isNotEnd = Increment()) && (char.IsWhiteSpace(_currentChar) || _currentChar == ',')) { } + return isNotEnd; + } + private bool Increment() + { + if (_jsonLength > 1) + { + _jsonPtr++; + _jsonLength--; + _currentChar = _jsonPtr[0]; + return true; + } + return false; + } + private void Deincrement() + { + _jsonPtr--; + _jsonLength++; + _currentChar = _jsonPtr[0]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsEnterChar(char chr) { return chr == '{' || chr == '['; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsExitChar(char chr) { return chr == '}' || chr == ']'; } + } + internal readonly unsafe ref struct JsonNode + { + public readonly JsonNodeType Type; + public readonly char* Ptr; + public readonly int Length; + public readonly int Depth; + public bool IsEnter + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Type == JsonNodeType.Enter; } + } + public bool IsExit + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Type == JsonNodeType.Exit; } + } + public bool IsKey + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Type == JsonNodeType.Key; } + } + public bool IsValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Type == JsonNodeType.Value; } + } + public bool IsNotBreak + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Type != JsonNodeType.NA; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public JsonNode(JsonNodeType type, char* ptr, int length, int depth) + { + Type = type; + Ptr = ptr; + Length = length; + Depth = depth; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() { return new string(Ptr, 0, Length); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool EqualsString(string str) + { + if (str.Length != Length) { return false; } + for (int i = 0; i < str.Length; i++) + { + if (str[i] != Ptr[i]) { return false; } + } + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator bool(JsonNode node) { return node.IsNotBreak; } + } + internal enum JsonNodeType : byte + { + NA = 0, + Key = 1, Value = 2, + Enter = 3, Exit = 4, + } +} \ No newline at end of file diff --git a/src/Debug/DragonDocs/JsonStreamReader.cs b/src/Debug/DragonDocs/JsonStreamReader.cs new file mode 100644 index 0000000..5f6f02e --- /dev/null +++ b/src/Debug/DragonDocs/JsonStreamReader.cs @@ -0,0 +1,14 @@ +using System.IO; + +namespace DCFApixels.DragonECS.Internal +{ + internal struct JsonStreamReader + { + public StreamReader _stream; + private static char[] _chars; + public void Next() + { + _stream.Read(_chars, 0, 100); + } + } +} diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 4744906..ad71481 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -1,8 +1,5 @@ -using DCFApixels.DragonECS.Internal; -using System; +using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Reflection; namespace DCFApixels.DragonECS @@ -10,7 +7,6 @@ namespace DCFApixels.DragonECS public static class EcsDebugUtility { private const BindingFlags RFL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - private static readonly Dictionary _metaCache = new Dictionary(); #region GetGenericTypeName public static string GetGenericTypeFullName(int maxDepth = 2) @@ -275,20 +271,15 @@ namespace DCFApixels.DragonECS #region GetTypeMeta public static TypeMeta GetTypeMeta(object obj) { - return GetTypeMeta(GetTypeMetaSource(obj).GetType()); + return TypeMeta.Get(GetTypeMetaSource(obj).GetType()); } public static TypeMeta GetTypeMeta() { - return GetTypeMeta(typeof(T)); + return TypeMeta.Get(typeof(T)); } public static TypeMeta GetTypeMeta(Type type) { - if (_metaCache.TryGetValue(type, out TypeMeta result) == false) - { - result = new TypeMeta(type); - _metaCache.Add(type, result); - } - return result; + return TypeMeta.Get(type); } #endregion @@ -304,312 +295,6 @@ namespace DCFApixels.DragonECS #endregion } - public interface ITypeMeta - { - Type Type { get; } - string Name { get; } - MetaColor Color { get; } - MetaDescription Description { get; } - MetaGroup Group { get; } - IReadOnlyCollection Tags { get; } - } - - [DebuggerTypeProxy(typeof(DebuggerProxy))] - public sealed class TypeMeta : ITypeMeta - { - internal readonly Type _type; - - private bool _isCustomName; - private bool _isCustomColor; - private bool _isHidden; - - private string _name; - private MetaColor _color; - private MetaDescription _description; - private MetaGroup _group; - private IReadOnlyCollection _tags; - private int _typeCode; - - private InitFlag _initFlags = InitFlag.None; - - #region Constructors - public TypeMeta(Type type) - { - _type = type; - } - #endregion - - #region Type - public Type Type - { - get { return _type; } - } - #endregion - - #region Name - private void InitName() - { - if (_initFlags.HasFlag(InitFlag.Name) == false) - { - (_name, _isCustomName) = MetaGenerator.GetMetaName(_type); - _initFlags |= InitFlag.Name; - } - } - public bool IsCustomName - { - get - { - InitName(); - return _isCustomName; - } - } - public string Name - { - get - { - InitName(); - return _name; - } - } - #endregion - - #region Color - private void InitColor() - { - if (_initFlags.HasFlag(InitFlag.Color) == false) - { - (_color, _isCustomColor) = MetaGenerator.GetColor(_type); - _initFlags |= InitFlag.Color; - } - } - public bool IsCustomColor - { - get - { - InitColor(); - return _isCustomColor; - } - } - public MetaColor Color - { - get - { - InitColor(); - return _color; - } - } - #endregion - - #region Description - public MetaDescription Description - { - get - { - if (_initFlags.HasFlag(InitFlag.Description) == false) - { - _description = MetaGenerator.GetDescription(_type); - _initFlags |= InitFlag.Description; - } - return _description; - } - } - #endregion - - #region Group - public MetaGroup Group - { - get - { - if (_initFlags.HasFlag(InitFlag.Group) == false) - { - _group = MetaGenerator.GetGroup(_type); - _initFlags |= InitFlag.Group; - } - return _group; - } - } - #endregion - - #region Tags - private void InitTags() - { - if (_initFlags.HasFlag(InitFlag.Tags) == false) - { - _tags = MetaGenerator.GetTags(_type); - _initFlags |= InitFlag.Tags; - _isHidden = _tags.Contains(MetaTags.HIDDEN); - } - } - public IReadOnlyCollection Tags - { - get - { - InitTags(); - return _tags; - } - } - public bool IsHidden - { - get - { - InitTags(); - return _isHidden; - } - } - #endregion - - #region TypeCode - public int TypeCode - { - get - { - if (_initFlags.HasFlag(InitFlag.TypeCode) == false) - { - _typeCode = EcsTypeCode.Get(_type); - _initFlags |= InitFlag.TypeCode; - } - return _typeCode; - } - } - #endregion - - #region InitializeAll - public void InitializeAll() - { - if (_initFlags == InitFlag.All) - { - return; - } - _ = Name; - _ = Group; - _ = Color; - _ = Description; - _ = Tags; - _ = TypeCode; - } - #endregion - - #region InitFlag - [Flags] - private enum InitFlag : byte - { - None = 0, - Name = 1 << 0, - Group = 1 << 1, - Color = 1 << 2, - Description = 1 << 3, - Tags = 1 << 4, - TypeCode = 1 << 5, - - All = Name | Group | Color | Description | Tags | TypeCode - } - #endregion - - #region Other - public override string ToString() - { - return Name; - } - private class DebuggerProxy : ITypeMeta - { - private readonly TypeMeta _meta; - public Type Type - { - get { return _meta.Type; } - } - public string Name - { - get { return _meta.Name; } - } - public MetaColor Color - { - get { return _meta.Color; } - } - public MetaDescription Description - { - get { return _meta.Description; } - } - public MetaGroup Group - { - get { return _meta.Group; } - } - public IReadOnlyCollection Tags - { - get { return _meta.Tags; } - } - public DebuggerProxy(TypeMeta meta) - { - _meta = meta; - } - } - #endregion - - #region MetaGenerator - private static class MetaGenerator - { - private const int GENERIC_NAME_DEPTH = 3; - - #region GetMetaName - public static (string, bool) GetMetaName(Type type) - { - bool isCustom = type.TryGetCustomAttribute(out MetaNameAttribute atr) && string.IsNullOrEmpty(atr.name) == false; - if (isCustom) - { - if ((type.IsGenericType && atr.isHideGeneric == false) == false) - { - return (atr.name, isCustom); - } - string genericParams = ""; - Type[] typeParameters = type.GetGenericArguments(); - for (int i = 0; i < typeParameters.Length; ++i) - { - string paramTypeName = EcsDebugUtility.GetGenericTypeName(typeParameters[i], GENERIC_NAME_DEPTH); - genericParams += (i == 0 ? paramTypeName : $", {paramTypeName}"); - } - return ($"{atr.name}<{genericParams}>", isCustom); - } - return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), isCustom); - } - #endregion - - #region GetColor - private static MetaColor AutoColor(Type type) - { - return new MetaColor(type.Name).UpContrast();//.Desaturate(0.48f) / 1.18f; - } - public static (MetaColor, bool) GetColor(Type type) - { - bool isCustom = type.TryGetCustomAttribute(out MetaColorAttribute atr); - return (isCustom ? atr.color : AutoColor(type), isCustom); - } - #endregion - - #region GetGroup - public static MetaGroup GetGroup(Type type) - { - return type.TryGetCustomAttribute(out MetaGroupAttribute atr) ? atr.Data : MetaGroup.Empty; - } - #endregion - - #region GetDescription - public static MetaDescription GetDescription(Type type) - { - bool isCustom = type.TryGetCustomAttribute(out MetaDescriptionAttribute atr); - return isCustom ? atr.Data : MetaDescription.Empty; - } - #endregion - - #region GetTags - public static IReadOnlyCollection GetTags(Type type) - { - var atr = type.GetCustomAttribute(); - return atr != null ? atr.Tags : Array.Empty(); - } - #endregion - } - #endregion - } - public static class TypeMetaDataCachedExtensions { public static TypeMeta GetMeta(this object self) diff --git a/src/Debug/MetaAttributes/MetaColorAttribute.cs b/src/Debug/MetaAttributes/MetaColorAttribute.cs index 537c8b9..0623e39 100644 --- a/src/Debug/MetaAttributes/MetaColorAttribute.cs +++ b/src/Debug/MetaAttributes/MetaColorAttribute.cs @@ -1,5 +1,6 @@ using DCFApixels.DragonECS.Internal; using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS @@ -339,7 +340,61 @@ namespace DCFApixels.DragonECS } #endregion - public override string ToString() { return $"({r}, {g}, {b})"; } + public override string ToString() { return $"({r}, {g}, {b}, {a})"; } + public unsafe static MetaColor Parse(string input) + { + if (input == null) { throw new ArgumentNullException(nameof(input)); } + int length = input.Length; + fixed (char* f = input) + { + return Parse(f, length); + } + } + public unsafe static MetaColor Parse(char* ptr, int length) + { + if (ptr == null) { throw new ArgumentNullException(nameof(ptr)); } + byte r, g, b, a; + Trim(ref ptr, ref length); + r = ParseByte(ref ptr, ref length); + g = ParseByte(ref ptr, ref length); + b = ParseByte(ref ptr, ref length); + a = ParseByte(ref ptr, ref length, true); + return new MetaColor(r, g, b, a); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static void Trim(ref char* span, ref int length) + { + if (!char.IsWhiteSpace(span[0]) && !char.IsWhiteSpace(span[length - 1])) { return; } + while (char.IsWhiteSpace(span[0])) { span++; length--; } + while (char.IsWhiteSpace(span[--length])) { } + length++; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static byte ParseByte(ref char* ptr, ref int length, bool isAplha = false) + { + if (length <= 0) { return isAplha ? byte.MaxValue : byte.MinValue; } + while (char.IsDigit(*ptr) == false) + { + ptr++; length--; + if (length <= 0) { return isAplha ? byte.MaxValue : byte.MinValue; } + } + + int value = 0; + while (char.IsDigit(*ptr)) + { + value = value * 10 + (*ptr - '0'); + ptr++; length--; + if (length <= 0) { break; } + } + + if (value < 0 || value > 255) + { + throw new FormatException("Color component value should be between 0 and 255."); + } + return (byte)value; + } + + public bool Equals(MetaColor other) { return colorCode == other.colorCode; } public override bool Equals(object obj) { return obj is MetaColor other && Equals(other); } public override int GetHashCode() { return colorCode; } diff --git a/src/Debug/TypeMeta.cs b/src/Debug/TypeMeta.cs new file mode 100644 index 0000000..0c98b93 --- /dev/null +++ b/src/Debug/TypeMeta.cs @@ -0,0 +1,326 @@ +using DCFApixels.DragonECS.Internal; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace DCFApixels.DragonECS +{ + public interface ITypeMeta + { + Type Type { get; } + string Name { get; } + MetaColor Color { get; } + MetaDescription Description { get; } + MetaGroup Group { get; } + IReadOnlyCollection Tags { get; } + } + + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public sealed class TypeMeta : ITypeMeta + { + private static readonly Dictionary _metaCache = new Dictionary(); + + internal readonly Type _type; + + private bool _isCustomName; + private bool _isCustomColor; + private bool _isHidden; + + private string _name; + private MetaColor _color; + private MetaDescription _description; + private MetaGroup _group; + private IReadOnlyCollection _tags; + private int _typeCode; + + private InitFlag _initFlags = InitFlag.None; + + #region Constructors + public static TypeMeta Get(Type type) + { + if (_metaCache.TryGetValue(type, out TypeMeta result) == false) + { + result = new TypeMeta(type); + _metaCache.Add(type, result); + } + return result; + } + private TypeMeta(Type type) + { + _type = type; + } + #endregion + + #region Type + public Type Type + { + get { return _type; } + } + #endregion + + #region Name + private void InitName() + { + if (_initFlags.HasFlag(InitFlag.Name) == false) + { + (_name, _isCustomName) = MetaGenerator.GetMetaName(_type); + _initFlags |= InitFlag.Name; + } + } + public bool IsCustomName + { + get + { + InitName(); + return _isCustomName; + } + } + public string Name + { + get + { + InitName(); + return _name; + } + } + #endregion + + #region Color + private void InitColor() + { + if (_initFlags.HasFlag(InitFlag.Color) == false) + { + (_color, _isCustomColor) = MetaGenerator.GetColor(_type); + _initFlags |= InitFlag.Color; + } + } + public bool IsCustomColor + { + get + { + InitColor(); + return _isCustomColor; + } + } + public MetaColor Color + { + get + { + InitColor(); + return _color; + } + } + #endregion + + #region Description + public MetaDescription Description + { + get + { + if (_initFlags.HasFlag(InitFlag.Description) == false) + { + _description = MetaGenerator.GetDescription(_type); + _initFlags |= InitFlag.Description; + } + return _description; + } + } + #endregion + + #region Group + public MetaGroup Group + { + get + { + if (_initFlags.HasFlag(InitFlag.Group) == false) + { + _group = MetaGenerator.GetGroup(_type); + _initFlags |= InitFlag.Group; + } + return _group; + } + } + #endregion + + #region Tags + private void InitTags() + { + if (_initFlags.HasFlag(InitFlag.Tags) == false) + { + _tags = MetaGenerator.GetTags(_type); + _initFlags |= InitFlag.Tags; + _isHidden = _tags.Contains(MetaTags.HIDDEN); + } + } + public IReadOnlyCollection Tags + { + get + { + InitTags(); + return _tags; + } + } + public bool IsHidden + { + get + { + InitTags(); + return _isHidden; + } + } + #endregion + + #region TypeCode + public int TypeCode + { + get + { + if (_initFlags.HasFlag(InitFlag.TypeCode) == false) + { + _typeCode = EcsTypeCode.Get(_type); + _initFlags |= InitFlag.TypeCode; + } + return _typeCode; + } + } + #endregion + + #region InitializeAll + public TypeMeta InitializeAll() + { + if (_initFlags != InitFlag.All) + { + _ = Name; + _ = Group; + _ = Color; + _ = Description; + _ = Tags; + _ = TypeCode; + } + return this; + } + #endregion + + #region InitFlag + [Flags] + private enum InitFlag : byte + { + None = 0, + Name = 1 << 0, + Group = 1 << 1, + Color = 1 << 2, + Description = 1 << 3, + Tags = 1 << 4, + TypeCode = 1 << 5, + + All = Name | Group | Color | Description | Tags | TypeCode + } + #endregion + + #region Other + public override string ToString() + { + return Name; + } + private class DebuggerProxy : ITypeMeta + { + private readonly TypeMeta _meta; + public Type Type + { + get { return _meta.Type; } + } + public string Name + { + get { return _meta.Name; } + } + public MetaColor Color + { + get { return _meta.Color; } + } + public MetaDescription Description + { + get { return _meta.Description; } + } + public MetaGroup Group + { + get { return _meta.Group; } + } + public IReadOnlyCollection Tags + { + get { return _meta.Tags; } + } + public DebuggerProxy(TypeMeta meta) + { + _meta = meta; + } + } + #endregion + + #region MetaGenerator + private static class MetaGenerator + { + private const int GENERIC_NAME_DEPTH = 3; + + #region GetMetaName + public static (string, bool) GetMetaName(Type type) + { + bool isCustom = type.TryGetCustomAttribute(out MetaNameAttribute atr) && string.IsNullOrEmpty(atr.name) == false; + if (isCustom) + { + if ((type.IsGenericType && atr.isHideGeneric == false) == false) + { + return (atr.name, isCustom); + } + string genericParams = ""; + Type[] typeParameters = type.GetGenericArguments(); + for (int i = 0; i < typeParameters.Length; ++i) + { + string paramTypeName = EcsDebugUtility.GetGenericTypeName(typeParameters[i], GENERIC_NAME_DEPTH); + genericParams += (i == 0 ? paramTypeName : $", {paramTypeName}"); + } + return ($"{atr.name}<{genericParams}>", isCustom); + } + return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), isCustom); + } + #endregion + + #region GetColor + private static MetaColor AutoColor(Type type) + { + return new MetaColor(type.Name).UpContrast();//.Desaturate(0.48f) / 1.18f; + } + public static (MetaColor, bool) GetColor(Type type) + { + bool isCustom = type.TryGetCustomAttribute(out MetaColorAttribute atr); + return (isCustom ? atr.color : AutoColor(type), isCustom); + } + #endregion + + #region GetGroup + public static MetaGroup GetGroup(Type type) + { + return type.TryGetCustomAttribute(out MetaGroupAttribute atr) ? atr.Data : MetaGroup.Empty; + } + #endregion + + #region GetDescription + public static MetaDescription GetDescription(Type type) + { + bool isCustom = type.TryGetCustomAttribute(out MetaDescriptionAttribute atr); + return isCustom ? atr.Data : MetaDescription.Empty; + } + #endregion + + #region GetTags + public static IReadOnlyCollection GetTags(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? atr.Tags : Array.Empty(); + } + #endregion + } + #endregion + } +} \ No newline at end of file diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index c8037c5..16de0cb 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -1,4 +1,5 @@ using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.PoolsCore; using DCFApixels.DragonECS.RunnersCore; using System; using System.Linq; @@ -6,7 +7,7 @@ using static DCFApixels.DragonECS.EcsDebugUtility; namespace DCFApixels.DragonECS { - public interface IEcsProcess { } + public interface IEcsProcess : IEcsMember { } namespace RunnersCore { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index a4ec473..cbc3d27 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -36,7 +36,7 @@ namespace DCFApixels.DragonECS [Il2CppSetOption(Option.ArrayBoundsChecks, false)] #endif [DebuggerTypeProxy(typeof(DebuggerProxy))] - public partial class EcsWorld : IEntityStorage + public partial class EcsWorld : IEntityStorage, IEcsMember { public readonly short id; private IConfigContainer _configs; diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index c7037a5..766cc24 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -11,7 +11,7 @@ using Unity.IL2CPP.CompilerServices; namespace DCFApixels.DragonECS { /// Standard component - public interface IEcsComponent : IEcsComponentType { } + public interface IEcsComponent : IEcsMember { } #if ENABLE_IL2CPP [Il2CppSetOption (Option.NullChecks, false)] diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index c8f54af..d197729 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS.PoolsCore { - public interface IEcsComponentType { } + public interface IEcsMember { } /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. public interface IEcsPoolImplementation : IEcsPool { diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 7dc6b90..3ce5b5b 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -11,7 +11,7 @@ using Unity.IL2CPP.CompilerServices; namespace DCFApixels.DragonECS { /// Component without data - public interface IEcsTagComponent : IEcsComponentType { } + public interface IEcsTagComponent : IEcsMember { } #if ENABLE_IL2CPP [Il2CppSetOption (Option.NullChecks, false)]