EcsComponent.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // ----------------------------------------------------------------------------
  2. // The MIT License
  3. // Simple Entity Component System framework https://github.com/Leopotam/ecs
  4. // Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
  5. // ----------------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Runtime.CompilerServices;
  9. using System.Threading;
  10. // ReSharper disable ClassNeverInstantiated.Global
  11. namespace Leopotam.Ecs {
  12. /// <summary>
  13. /// Marks component type to be not auto-filled as GetX in filter.
  14. /// </summary>
  15. public interface IEcsIgnoreInFilter { }
  16. /// <summary>
  17. /// Marks component type as resettable with custom logic.
  18. /// </summary>
  19. public interface IEcsAutoReset {
  20. void Reset ();
  21. }
  22. /// <summary>
  23. /// Marks field of IEcsSystem class to be ignored during dependency injection.
  24. /// </summary>
  25. public sealed class EcsIgnoreInjectAttribute : Attribute { }
  26. /// <summary>
  27. /// Marks field of component to be not checked for null on component removing.
  28. /// Works only in DEBUG mode!
  29. /// </summary>
  30. [System.Diagnostics.Conditional ("DEBUG")]
  31. [AttributeUsage (AttributeTargets.Field)]
  32. public sealed class EcsIgnoreNullCheckAttribute : Attribute { }
  33. /// <summary>
  34. /// Global descriptor of used component type.
  35. /// </summary>
  36. /// <typeparam name="T">Component type.</typeparam>
  37. public static class EcsComponentType<T> where T : class {
  38. // ReSharper disable StaticMemberInGenericType
  39. public static readonly int TypeIndex;
  40. public static readonly Type Type;
  41. public static readonly bool IsAutoReset;
  42. public static readonly bool IsIgnoreInFilter;
  43. // ReSharper restore StaticMemberInGenericType
  44. static EcsComponentType () {
  45. TypeIndex = Interlocked.Increment (ref EcsComponentPool.ComponentTypesCount);
  46. Type = typeof (T);
  47. IsAutoReset = typeof (IEcsAutoReset).IsAssignableFrom (Type);
  48. IsIgnoreInFilter = typeof (IEcsIgnoreInFilter).IsAssignableFrom (Type);
  49. }
  50. }
  51. #if ENABLE_IL2CPP
  52. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  53. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  54. #endif
  55. public sealed class EcsComponentPool {
  56. /// <summary>
  57. /// Global component type counter.
  58. /// First component will be "1" for correct filters updating (add component on positive and remove on negative).
  59. /// </summary>
  60. internal static int ComponentTypesCount;
  61. #if DEBUG
  62. readonly List<System.Reflection.FieldInfo> _nullableFields = new List<System.Reflection.FieldInfo> (8);
  63. #endif
  64. public object[] Items = new Object[128];
  65. Func<object> _customCtor;
  66. readonly Type _type;
  67. readonly bool _isAutoReset;
  68. int[] _reservedItems = new int[128];
  69. int _itemsCount;
  70. int _reservedItemsCount;
  71. internal EcsComponentPool (Type cType, bool isAutoReset) {
  72. _type = cType;
  73. _isAutoReset = isAutoReset;
  74. #if DEBUG
  75. // collect all marshal-by-reference fields.
  76. var fields = _type.GetFields ();
  77. for (var i = 0; i < fields.Length; i++) {
  78. var field = fields[i];
  79. if (!Attribute.IsDefined (field, typeof (EcsIgnoreNullCheckAttribute))) {
  80. var type = field.FieldType;
  81. var underlyingType = Nullable.GetUnderlyingType (type);
  82. if (!type.IsValueType || (underlyingType != null && !underlyingType.IsValueType)) {
  83. if (type != typeof (string)) {
  84. _nullableFields.Add (field);
  85. }
  86. }
  87. if (type == typeof (EcsEntity)) {
  88. _nullableFields.Add (field);
  89. }
  90. }
  91. }
  92. #endif
  93. }
  94. /// <summary>
  95. /// Sets custom constructor for component instances.
  96. /// </summary>
  97. /// <param name="ctor"></param>
  98. public void SetCustomCtor (Func<object> ctor) {
  99. #if DEBUG
  100. // ReSharper disable once JoinNullCheckWithUsage
  101. if (ctor == null) { throw new Exception ("Ctor is null."); }
  102. #endif
  103. _customCtor = ctor;
  104. }
  105. /// <summary>
  106. /// Sets new capacity (if more than current amount).
  107. /// </summary>
  108. /// <param name="capacity">New value.</param>
  109. public void SetCapacity (int capacity) {
  110. if (capacity > Items.Length) {
  111. Array.Resize (ref Items, capacity);
  112. }
  113. }
  114. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  115. public int New () {
  116. int id;
  117. if (_reservedItemsCount > 0) {
  118. id = _reservedItems[--_reservedItemsCount];
  119. } else {
  120. id = _itemsCount;
  121. if (_itemsCount == Items.Length) {
  122. Array.Resize (ref Items, _itemsCount << 1);
  123. }
  124. var instance = _customCtor != null ? _customCtor () : Activator.CreateInstance (_type);
  125. // reset brand new instance if component implements IEcsAutoReset.
  126. if (_isAutoReset) {
  127. ((IEcsAutoReset) instance).Reset ();
  128. }
  129. Items[_itemsCount++] = instance;
  130. }
  131. return id;
  132. }
  133. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  134. public object GetItem (int idx) {
  135. return Items[idx];
  136. }
  137. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  138. public void Recycle (int idx) {
  139. if (_isAutoReset) {
  140. ((IEcsAutoReset) Items[idx]).Reset ();
  141. }
  142. #if DEBUG
  143. // check all marshal-by-reference typed fields for nulls.
  144. var obj = Items[idx];
  145. for (int i = 0, iMax = _nullableFields.Count; i < iMax; i++) {
  146. if (_nullableFields[i].FieldType.IsValueType) {
  147. if (_nullableFields[i].FieldType == typeof (EcsEntity) && ((EcsEntity) _nullableFields[i].GetValue (obj)).Owner != null) {
  148. throw new Exception (
  149. $"Memory leak for \"{_type.Name}\" component: \"{_nullableFields[i].Name}\" field not null-ed with EcsEntity.Null. If you are sure that it's not - mark field with [EcsIgnoreNullCheck] attribute.");
  150. }
  151. } else {
  152. if (_nullableFields[i].GetValue (obj) != null) {
  153. throw new Exception (
  154. $"Memory leak for \"{_type.Name}\" component: \"{_nullableFields[i].Name}\" field not null-ed. If you are sure that it's not - mark field with [EcsIgnoreNullCheck] attribute.");
  155. }
  156. }
  157. }
  158. #endif
  159. if (_reservedItemsCount == _reservedItems.Length) {
  160. Array.Resize (ref _reservedItems, _reservedItemsCount << 1);
  161. }
  162. _reservedItems[_reservedItemsCount++] = idx;
  163. }
  164. }
  165. }