A stable modding interface between Techblox and mods https://mod.exmods.org/
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

133 řádky
4.7KB

  1. using System;
  2. using System.Collections.Generic;
  3. using Svelto.DataStructures;
  4. using Svelto.ECS;
  5. using Svelto.ECS.Hybrid;
  6. using Svelto.ECS.Internal;
  7. using TechbloxModdingAPI.Common.Engines;
  8. using TechbloxModdingAPI.Common.Utils;
  9. using TechbloxModdingAPI.Utility;
  10. namespace TechbloxModdingAPI.Common;
  11. public abstract class EcsObjectBase
  12. {
  13. public EGID Id => _engine.GetEgid(Reference);
  14. /// <summary>
  15. /// A reference to a specific entity that persists through group swaps and such.
  16. /// May be an invalid reference, in that case operations do not have any effect.
  17. /// </summary>
  18. public EntityReference Reference { get; }
  19. /// <summary>
  20. /// Whether the entity reference is still valid. Returns false if this object no longer exists.
  21. /// </summary>
  22. public bool Exists => Id != default; // TODO: Might need extra code to support IDs during init
  23. private static readonly Dictionary<Type, WeakDictionary<EntityReference, EcsObjectBase>> _instances = new();
  24. private static readonly EcsObjectBaseEngine _engine = new();
  25. private static WeakDictionary<EntityReference, EcsObjectBase> GetInstances(Type type)
  26. {
  27. return _instances.TryGetValue(type, out var dict) ? dict : null;
  28. }
  29. /// <summary>
  30. /// Returns a cached instance if there's an actively used instance of the object already.
  31. /// Objects still get garbage collected and then they will be removed from the cache.
  32. /// </summary>
  33. /// <param name="egid">The EGID of the entity</param>
  34. /// <param name="constructor">The constructor to construct the object</param>
  35. /// <typeparam name="T">The object type</typeparam>
  36. /// <returns></returns>
  37. internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase
  38. {
  39. var instances = GetInstances(type ?? typeof(T));
  40. if (instances == null || !instances.TryGetValue(_engine.GetEntityReference(egid), out var instance))
  41. return constructor(egid); // It will be added by the constructor
  42. return (T)instance;
  43. }
  44. protected EcsObjectBase(EGID id, Type entityDescriptorType) : this(_engine.GetEntityReference(id), entityDescriptorType)
  45. {
  46. }
  47. protected EcsObjectBase(EntityReference reference, Type entityDescriptorType)
  48. {
  49. if (!_instances.TryGetValue(GetType(), out var dict))
  50. {
  51. dict = new();
  52. _instances.Add(GetType(), dict);
  53. }
  54. if (!dict.ContainsKey(reference)) // Multiple instances may be created
  55. dict.Add(reference, this);
  56. Reference = reference;
  57. }
  58. protected internal OptionalRef<T> GetComponentOptional<T>() where T : unmanaged, IEntityComponent
  59. {
  60. return _engine.GetComponentOptional<T>(this);
  61. }
  62. protected internal ref T GetComponent<T>() where T : unmanaged, IEntityComponent
  63. {
  64. return ref _engine.GetComponent<T>(this);
  65. }
  66. protected internal ref T GetViewComponent<T>() where T : struct, IEntityViewComponent
  67. {
  68. return ref _engine.GetViewComponent<T>(this);
  69. }
  70. protected internal object GetComponent(Type type, string name)
  71. {
  72. return _engine.GetComponent(this, type, name);
  73. }
  74. protected internal void SetComponent(Type type, string name, object value)
  75. {
  76. _engine.SetComponent(this, type, name, value);
  77. }
  78. protected bool RemoveEntity()
  79. {
  80. // TODO: _entityFunctions.Remove...()
  81. }
  82. #region ECS initializer stuff
  83. protected internal EcsInitData InitData;
  84. /// <summary>
  85. /// Holds information needed to construct a component initializer.
  86. /// Necessary because the initializer is a ref struct which cannot be assigned to a field.
  87. /// </summary>
  88. protected internal struct EcsInitData
  89. {
  90. private FasterDictionary<RefWrapperType, ITypeSafeDictionary> group;
  91. private EntityReference reference;
  92. public static implicit operator EcsInitData(EntityInitializer initializer) => new()
  93. { group = GetInitGroup(initializer), reference = initializer.reference };
  94. public EntityInitializer Initializer(EGID id) => new(id, group, reference);
  95. public bool Valid => group != null;
  96. }
  97. private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc(
  98. EntityInitializer initializer);
  99. /// <summary>
  100. /// Accesses the group field of the initializer
  101. /// </summary>
  102. private static readonly GetInitGroupFunc GetInitGroup = Reflections.CreateAccessor<GetInitGroupFunc>("_group");
  103. #endregion
  104. public static void Init()
  105. {
  106. EngineManager.AddEngine(_engine, ApiEngineType.Build, ApiEngineType.Menu, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
  107. }
  108. }