oecs-sharp/OECS/Entity.cs

83 lines
2.5 KiB
C#
Raw Permalink Normal View History

using MessagePack;
namespace OECS;
/// <summary>
/// An opaque handle to an entity in the ECS world.
/// Combines a 24-bit identifier with an 8-bit version to prevent
/// use-after-free bugs when entity IDs are recycled.
/// </summary>
[MessagePackObject(AllowPrivate = true)]
[MessagePackFormatter(typeof(EntityFormatter))]
public readonly partial struct Entity : IEquatable<Entity>
{
private const uint IdMask = 0x00FF_FFFF; // lower 24 bits
private const uint VersionMask = 0xFF00_0000; // upper 8 bits
private const int VersionShift = 24;
private const uint MaxId = IdMask;
private const uint MaxVersion = 0xFF;
/// <summary>
/// A sentinel value representing a null or invalid entity.
/// </summary>
public static readonly Entity Null = new(0);
[Key(0)]
private readonly uint _value;
internal Entity(uint id, uint version)
{
_value = (id & IdMask) | ((version & MaxVersion) << VersionShift);
}
private Entity(uint value)
{
_value = value;
}
/// <summary>
/// The 24-bit entity identifier.
/// </summary>
[IgnoreMember]
public uint Id => _value & IdMask;
/// <summary>
/// The 8-bit generation version. Incremented each time the ID is recycled.
/// </summary>
[IgnoreMember]
public uint Version => (_value & VersionMask) >> VersionShift;
/// <summary>
/// Returns true if this entity is the null sentinel.
/// </summary>
[IgnoreMember]
public bool IsNull => _value == 0;
/// <summary>
/// Creates a new entity with the given version, keeping the same ID.
/// </summary>
internal Entity WithVersion(uint version) => new(Id, version);
/// <summary>
/// Converts the entity to its raw 32-bit packed representation.
/// </summary>
public static explicit operator uint(Entity entity) => entity._value;
/// <summary>
/// Creates an entity from its raw 32-bit packed representation.
/// </summary>
public static explicit operator Entity(uint value) => new(value);
public bool Equals(Entity other) => _value == other._value;
public override bool Equals(object? obj) => obj is Entity other && Equals(other);
public override int GetHashCode() => _value.GetHashCode();
public override string ToString() => $"Entity({Id}:v{Version})";
public static bool operator ==(Entity left, Entity right) => left.Equals(right);
public static bool operator !=(Entity left, Entity right) => !left.Equals(right);
}