|
- using System;
- using System.Linq;
- using Svelto.DataStructures;
- using Svelto.ECS;
- using TechbloxModdingAPI.Common;
-
- namespace TechbloxModdingAPI.Utility
- {
- public ref struct OptionalRef<T> where T : struct, IBaseEntityComponent
- {
- private readonly EGID entityId;
- private readonly State state;
- private readonly uint index;
- private NB<T> array;
- private MB<T> managedArray;
- private readonly EntityInitializer initializer;
- //The possible fields are: (index && (array || managedArray)) || initializer
- private readonly EcsObjectBase obj;
-
- public OptionalRef(NB<T> array, uint index, EGID entityId = default)
- {
- state = State.Native;
- this.array = array;
- this.index = index;
- this.entityId = entityId;
- initializer = default;
- managedArray = default;
- obj = default;
- }
-
- public OptionalRef(MB<T> array, uint index, EGID entityId = default)
- {
- state = State.Managed;
- managedArray = array;
- this.index = index;
- this.entityId = entityId;
- initializer = default;
- this.array = default;
- obj = default;
- }
-
- /// <summary>
- /// Wraps the initializer data, if present.
- /// </summary>
- /// <param name="obj">The object with the initializer</param>
- /// <param name="unmanaged">Whether the struct is unmanaged</param>
- public OptionalRef(EcsObjectBase obj, bool unmanaged, EGID entityId = default)
- {
- this.entityId = entityId;
- if (obj.InitData.Valid)
- {
- initializer = obj.InitData.Initializer(obj.Id);
- state = (unmanaged ? State.Native : State.Managed) | State.Initializer;
- }
- else
- {
- initializer = default;
- state = State.Empty;
- }
- array = default;
- index = default;
- managedArray = default;
- this.obj = obj;
- }
-
- /// <summary>
- /// Returns the value or a default value if empty. Supports objects that are being initialized.
- /// </summary>
- /// <returns>The value or the default value</returns>
- public ref T Get()
- {
- CompRefCache.Default = default; //The default value can be changed by mods
- if (state == State.Empty) return ref CompRefCache.Default;
- // If initializing the entity, check if the component is allowed by the descriptor, otherwise it could cause
- // issues in the game with Add() calls running unexpectedly
- if ((state & State.Initializer) != State.Empty && obj.AllowedEntityComponents.Contains(typeof(T)))
- return ref initializer.GetOrAdd<T>();
- if ((state & State.Native) != State.Empty) return ref array[index];
- return ref managedArray[index];
- }
-
- /// <summary>
- /// A non-by-ref view that allows getting and setting the value.
- /// </summary>
- public T Value
- {
- get => Get();
- set => Get() = value;
- }
-
- /// <summary>
- /// The ID of the entity this component belongs to or default if it doesn't exist.
- /// </summary>
- public EGID EGID => entityId;
-
- public bool Exists => state != State.Empty;
- public T? Nullable() => this ? Get() : default;
-
- public static implicit operator T(OptionalRef<T> opt) => opt.Get();
-
- public static implicit operator bool(OptionalRef<T> opt) => opt.state != State.Empty;
-
- public static implicit operator EGID(OptionalRef<T> opt) => opt.entityId;
-
- /// <summary>
- /// Creates an instance of a struct T that can be referenced.
- /// </summary>
- private struct CompRefCache
- {
- public static T Default;
- }
-
- /// <summary>
- /// A byte that holds state in its bits.
- /// </summary>
- [Flags]
- private enum State : byte
- {
- Empty,
- Native,
- Managed,
- Initializer = 4
- }
- }
- }
|