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.

116 lines
3.7KB

  1. using System;
  2. using Svelto.DataStructures;
  3. using Svelto.ECS;
  4. namespace TechbloxModdingAPI.Utility
  5. {
  6. public ref struct OptionalRef<T> where T : struct, IBaseEntityComponent
  7. {
  8. private readonly EGID entityId;
  9. private readonly State state;
  10. private readonly uint index;
  11. private NB<T> array;
  12. private MB<T> managedArray;
  13. private readonly EntityInitializer initializer;
  14. //The possible fields are: (index && (array || managedArray)) || initializer
  15. public OptionalRef(NB<T> array, uint index, EGID entityId = default)
  16. {
  17. state = State.Native;
  18. this.array = array;
  19. this.index = index;
  20. this.entityId = entityId;
  21. initializer = default;
  22. managedArray = default;
  23. }
  24. public OptionalRef(MB<T> array, uint index, EGID entityId = default)
  25. {
  26. state = State.Managed;
  27. managedArray = array;
  28. this.index = index;
  29. this.entityId = entityId;
  30. initializer = default;
  31. this.array = default;
  32. }
  33. /// <summary>
  34. /// Wraps the initializer data, if present.
  35. /// </summary>
  36. /// <param name="obj">The object with the initializer</param>
  37. /// <param name="unmanaged">Whether the struct is unmanaged</param>
  38. public OptionalRef(EcsObjectBase obj, bool unmanaged, EGID entityId = default)
  39. {
  40. this.entityId = entityId;
  41. if (obj.InitData.Valid)
  42. {
  43. initializer = obj.InitData.Initializer(obj.Id);
  44. state = (unmanaged ? State.Native : State.Managed) | State.Initializer;
  45. }
  46. else
  47. {
  48. initializer = default;
  49. state = State.Empty;
  50. }
  51. array = default;
  52. index = default;
  53. managedArray = default;
  54. }
  55. /// <summary>
  56. /// Returns the value or a default value if empty. Supports objects that are being initialized.
  57. /// </summary>
  58. /// <returns>The value or the default value</returns>
  59. public ref T Get()
  60. {
  61. CompRefCache.Default = default; //The default value can be changed by mods
  62. if (state == State.Empty) return ref CompRefCache.Default;
  63. if ((state & State.Initializer) != State.Empty) return ref initializer.GetOrAdd<T>();
  64. if ((state & State.Native) != State.Empty) return ref array[index];
  65. return ref managedArray[index];
  66. }
  67. /// <summary>
  68. /// A non-by-ref view that allows getting and setting the value.
  69. /// </summary>
  70. public T Value
  71. {
  72. get => Get();
  73. set => Get() = value;
  74. }
  75. /// <summary>
  76. /// The ID of the entity this component belongs to or default if it doesn't exist.
  77. /// </summary>
  78. public EGID EGID => entityId;
  79. public bool Exists => state != State.Empty;
  80. public T? Nullable() => this ? Get() : default;
  81. public static implicit operator T(OptionalRef<T> opt) => opt.Get();
  82. public static implicit operator bool(OptionalRef<T> opt) => opt.state != State.Empty;
  83. public static implicit operator EGID(OptionalRef<T> opt) => opt.entityId;
  84. /// <summary>
  85. /// Creates an instance of a struct T that can be referenced.
  86. /// </summary>
  87. private struct CompRefCache
  88. {
  89. public static T Default;
  90. }
  91. /// <summary>
  92. /// A byte that holds state in its bits.
  93. /// </summary>
  94. [Flags]
  95. private enum State : byte
  96. {
  97. Empty,
  98. Native,
  99. Managed,
  100. Initializer = 4
  101. }
  102. }
  103. }