A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

95 lines
3.9KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS;
  6. using Svelto.ECS.Internal;
  7. using TechbloxModdingAPI.Common.Utils;
  8. using TechbloxModdingAPI.Utility;
  9. namespace TechbloxModdingAPI
  10. {
  11. public abstract class EcsObjectBase
  12. {
  13. public EGID Id { get; }
  14. private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances = new();
  15. internal static WeakDictionary<EGID, EcsObjectBase> GetInstances(Type type)
  16. {
  17. return _instances.TryGetValue(type, out var dict) ? dict : null;
  18. }
  19. /// <summary>
  20. /// Returns a cached instance if there's an actively used instance of the object already.
  21. /// Objects still get garbage collected and then they will be removed from the cache.
  22. /// </summary>
  23. /// <param name="egid">The EGID of the entity</param>
  24. /// <param name="constructor">The constructor to construct the object</param>
  25. /// <typeparam name="T">The object type</typeparam>
  26. /// <returns></returns>
  27. internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
  28. {
  29. var instances = GetInstances(type ?? typeof(T));
  30. if (instances == null || !instances.TryGetValue(egid, out var instance))
  31. return constructor(egid); // It will be added by the constructor
  32. return (T)instance;
  33. }
  34. protected EcsObjectBase(EGID id, Type entityDescriptorType)
  35. {
  36. if (!_instances.TryGetValue(GetType(), out var dict))
  37. {
  38. dict = new WeakDictionary<EGID, EcsObjectBase>();
  39. _instances.Add(GetType(), dict);
  40. }
  41. if (!dict.ContainsKey(id)) // Multiple instances may be created
  42. dict.Add(id, this);
  43. Id = id;
  44. }
  45. private void AnalyzeEntityDescriptor(Type entityDescriptorType)
  46. {
  47. // TODO: Cache
  48. // TODO: This should be in BlockClassGenerator
  49. // TODO: Add support for creating/deleting entities (getting an up to date server/client engines root)
  50. var templateType = typeof(EntityDescriptorTemplate<>).MakeGenericType(entityDescriptorType);
  51. var getTemplateClass = Expression.Constant(templateType);
  52. var getDescriptorExpr = Expression.PropertyOrField(getTemplateClass, "descriptor");
  53. var getTemplateDescriptorExpr =
  54. Expression.Lambda<Func<IEntityDescriptor>>(getDescriptorExpr);
  55. var getTemplateDescriptor = getTemplateDescriptorExpr.Compile();
  56. var builders = getTemplateDescriptor().componentsToBuild;
  57. }
  58. #region ECS initializer stuff
  59. protected internal EcsInitData InitData;
  60. /// <summary>
  61. /// Holds information needed to construct a component initializer.
  62. /// Necessary because the initializer is a ref struct which cannot be assigned to a field.
  63. /// </summary>
  64. protected internal struct EcsInitData
  65. {
  66. private FasterDictionary<RefWrapperType, ITypeSafeDictionary> group;
  67. private EntityReference reference;
  68. public static implicit operator EcsInitData(EntityInitializer initializer) => new()
  69. { group = GetInitGroup(initializer), reference = initializer.reference };
  70. public EntityInitializer Initializer(EGID id) => new(id, group, reference);
  71. public bool Valid => group != null;
  72. }
  73. private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc(
  74. EntityInitializer initializer);
  75. /// <summary>
  76. /// Accesses the group field of the initializer
  77. /// </summary>
  78. private static readonly GetInitGroupFunc GetInitGroup = Reflections.CreateAccessor<GetInitGroupFunc>("_group");
  79. #endregion
  80. }
  81. }