This repository has been archived by the owner on Oct 22, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add EcsId.Entity / Pair and related changes
- Loading branch information
Showing
17 changed files
with
316 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace gaemstone.ECS | ||
{ | ||
public readonly partial struct EcsId | ||
{ | ||
[StructLayout(LayoutKind.Explicit)] | ||
public readonly struct Entity | ||
: IEquatable<Entity> | ||
, IComparable<Entity> | ||
{ | ||
/// <summary> | ||
/// A special entity value that represents "no entity". | ||
/// There may never be an entity alive with this value. | ||
/// This is equal to using <c>default(EcsEntity)</c>. | ||
/// </summary> | ||
public static readonly Entity None = default; | ||
|
||
|
||
/// <summary> | ||
/// The internal 64-bit value representing this entity. | ||
/// </summary> | ||
[FieldOffset(0)] public readonly ulong Value; | ||
|
||
/// <summary> | ||
/// The unique 32-bit integer identifier for this entity. | ||
/// Only one (alive) entity may have this identifier at a time. | ||
/// </summary> | ||
[FieldOffset(0)] public readonly uint ID; | ||
|
||
/// <summary> | ||
/// The generation of this entitiy. | ||
/// This is increased each time an entity is destroyed. | ||
/// </summary> | ||
[FieldOffset(4)] public readonly ushort Generation; | ||
|
||
|
||
public bool IsNone => ID == 0; | ||
|
||
|
||
public Entity(ulong value) : this() | ||
{ | ||
const ulong UNUSED_MASK = 0x00FF_0000_0000_0000; | ||
const ulong ROLE_MASK = 0xFF00_0000_0000_0000; | ||
if ((value & UNUSED_MASK) != 0L) throw new ArgumentException("Value has unused bits set", nameof(value)); | ||
if ((value & ROLE_MASK) != 0L) throw new ArgumentException("Value has role bits set", nameof(value)); | ||
Value = value; | ||
} | ||
|
||
public Entity(uint id, ushort generation) : this() | ||
{ | ||
if (id == 0) throw new ArgumentException("ID must be greater than 0", nameof(id)); | ||
ID = id; Generation = generation; | ||
} | ||
|
||
|
||
public bool Equals(Entity other) => Value == other.Value; | ||
public override bool Equals(object? obj) => (obj is Entity other) && Equals(other); | ||
public override int GetHashCode() => HashCode.Combine(Value); | ||
|
||
public static bool operator ==(Entity left, Entity right) => left.Equals(right); | ||
public static bool operator !=(Entity left, Entity right) => !left.Equals(right); | ||
|
||
public int CompareTo(Entity other) => Value.CompareTo(other.Value); | ||
|
||
public override string ToString() => $"Entity(Id=0x{ID:X},Generation=0x{Generation:X})"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
|
||
namespace gaemstone.ECS | ||
{ | ||
public readonly partial struct EcsId | ||
{ | ||
public readonly struct Pair | ||
: IEquatable<Pair> | ||
, IComparable<Pair> | ||
{ | ||
public readonly ulong Value; | ||
|
||
public uint Target => (uint)Value; | ||
public uint Relation => (uint)((Value >> 32) & 0xFFFFFF); | ||
|
||
|
||
public Pair(ulong value) : this() | ||
{ | ||
if (Value >> 56 != (ulong)EcsRole.Pair) throw new ArgumentException( | ||
"Role bits must be set to EcsRole.Pair", nameof(value)); | ||
Value = value; | ||
} | ||
|
||
public Pair(uint relation, uint target) | ||
{ | ||
if (relation > 0xFFFFFF) throw new ArgumentOutOfRangeException(nameof(relation), | ||
relation, "Relation must fit into 24 bits (be smaller or equal to 0xFFFFFF"); | ||
Value = (ulong)target | (relation << 32) | ((ulong)EcsRole.Pair << 56); | ||
} | ||
|
||
|
||
public bool Equals(Pair other) => Value == other.Value; | ||
public override bool Equals(object? obj) => (obj is Pair other) && Equals(other); | ||
public override int GetHashCode() => HashCode.Combine(Value); | ||
|
||
public static bool operator ==(Pair left, Pair right) => left.Equals(right); | ||
public static bool operator !=(Pair left, Pair right) => !left.Equals(right); | ||
|
||
public int CompareTo(Pair other) => Value.CompareTo(other.Value); | ||
|
||
public override string ToString() => $"Pair(Target=0x{Target:X},Relation=0x{Relation:X})"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,51 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
namespace gaemstone.ECS | ||
{ | ||
// ID Generation Role | ||
// |-------32-------|---16---|###|-8-| | ||
// =================================== | ||
// | Low 32 bits | High 32 bits | | ||
// =================================== | ||
|
||
// When Role == Pair, this is the layout: | ||
// Target Relation Role | ||
// |-------32-------|-----24-----|###| | ||
|
||
[StructLayout(LayoutKind.Explicit)] | ||
public readonly struct EcsId | ||
public readonly partial struct EcsId | ||
: IEquatable<EcsId> | ||
, IComparable<EcsId> | ||
{ | ||
public static readonly EcsId None = default; | ||
|
||
[FieldOffset(0)] public readonly ulong Value; | ||
|
||
[FieldOffset(0)] public readonly uint Low; | ||
[FieldOffset(4)] public readonly uint High; | ||
|
||
[FieldOffset(0)] public readonly uint ID; | ||
[FieldOffset(4)] public readonly ushort Generation; | ||
[FieldOffset(7)] public readonly EcsRole Role; | ||
|
||
public bool IsNone => (ID == 0); | ||
|
||
[FieldOffset(0)] readonly Entity _asEntity; | ||
[FieldOffset(0)] readonly Pair _asPair; | ||
|
||
|
||
EcsId(ulong value) : this() => Value = value; | ||
EcsId(uint low, uint high) : this() { Low = low; High = high; } | ||
|
||
public EcsId(uint id) : this(id, 0, EcsRole.None) { } | ||
public EcsId(uint id, EcsRole role) : this(id, 0, role) { } | ||
public EcsId(uint id, ushort generation) : this(id, generation, EcsRole.None) { } | ||
public EcsId(uint id, ushort generation, EcsRole role) : this() | ||
{ | ||
if (id == 0) throw new ArgumentOutOfRangeException(nameof(id), "ID must be greater than 0 to be valid."); | ||
ID = id; Generation = generation; Role = role; | ||
} | ||
|
||
public static EcsId Pair(uint relation, uint target) | ||
=> new(target | (relation & 0xFFFFFF) << 32 | (ulong)EcsRole.Pair << 56); | ||
public static EcsId Pair(EcsId relation, EcsId target) | ||
=> Pair(target.ID, relation.ID); | ||
public (uint Relation, uint Target) ToPair() | ||
=> (High & 0xFFFFFF, Low); | ||
public (EcsId Relation, EcsId Target) ToPair(Universe context) | ||
{ | ||
var (relationId, targetId) = ToPair(); | ||
if (!context.Entities.TryLookup(relationId, out var relation)) throw new EntityNotFoundException(relationId); | ||
if (!context.Entities.TryLookup(targetId , out var target )) throw new EntityNotFoundException(targetId); | ||
return (relation, target); | ||
} | ||
public static implicit operator EcsId(Entity entity) => new(entity.Value); | ||
public static implicit operator EcsId(Pair pair ) => new(pair.Value); | ||
|
||
public Entity? AsEntity() => (Role == EcsRole.Entity) ? _asEntity : null; | ||
public Pair? AsPair () => (Role == EcsRole.Pair ) ? _asPair : null; | ||
|
||
|
||
public bool Equals(EcsId other) => Value == other.Value; | ||
public override bool Equals(object? obj) => (obj is EcsId other) && Equals(other); | ||
public override int GetHashCode() => Value.GetHashCode(); | ||
|
||
public static bool operator ==(EcsId left, EcsId right) => left.Equals(right); | ||
public static bool operator ==(EcsId left, EcsId right) => left.Equals(right); | ||
public static bool operator !=(EcsId left, EcsId right) => !left.Equals(right); | ||
|
||
public int CompareTo(EcsId other) => Value.CompareTo(other.Value); | ||
|
||
|
||
public override string ToString() | ||
{ | ||
var builder = new StringBuilder(); | ||
AppendString(builder); | ||
return builder.ToString(); | ||
} | ||
public void AppendString(StringBuilder builder) | ||
{ | ||
if (Role == EcsRole.Pair) { | ||
builder.Append($"EcsId.Pair(relation: 0x{High & 0xFFFFFF:X}, target: 0x{Low:X})"); | ||
} else { | ||
builder.Append($"EcsId(id: 0x{ID:X}"); | ||
if (Generation != 0) builder.Append($", generation: {Generation}"); | ||
if (Role != EcsRole.None) builder.Append($", role: {Role}"); | ||
builder.Append(')'); | ||
} | ||
} | ||
|
||
public string ToString(Universe context) | ||
{ | ||
var builder = new StringBuilder(); | ||
AppendString(builder, context); | ||
return builder.ToString(); | ||
} | ||
public void AppendString(StringBuilder builder, Universe context) | ||
{ | ||
if (Role == EcsRole.Pair) { | ||
var (relationId, targetId) = ToPair(); | ||
builder.Append('('); | ||
if (context.Entities.TryLookup(relationId, out var relation)) relation.AppendString(builder); | ||
else builder.Append($"0x{relationId:X}"); | ||
builder.Append(","); | ||
if (context.Entities.TryLookup(targetId, out var target)) target.AppendString(builder); | ||
else builder.Append($"0x{targetId:X}"); | ||
builder.Append(')'); | ||
} else if (context.TryGet<Identifier>(this, out var identifier)) | ||
builder.Append(identifier); | ||
else | ||
AppendString(builder); | ||
} | ||
public override string ToString() => Role switch { | ||
EcsRole.Entity => _asEntity.ToString(), | ||
EcsRole.Pair => _asPair.ToString(), | ||
_ => $"EcsId(Value=0x{Value:X},Role={Role})" | ||
}; | ||
} | ||
|
||
public enum EcsRole : byte | ||
{ | ||
None, | ||
Entity, | ||
Pair, | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.