diff --git a/src/Arch.SourceGen/QueryGenerator.cs b/src/Arch.SourceGen/QueryGenerator.cs index 3c4ea85..68425ec 100644 --- a/src/Arch.SourceGen/QueryGenerator.cs +++ b/src/Arch.SourceGen/QueryGenerator.cs @@ -39,6 +39,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) references.AppendLine("using System;"); references.AppendLine("using System.Runtime.CompilerServices;"); references.AppendLine("using CommunityToolkit.HighPerformance;"); + references.AppendLine("using Arch.Core.Utils;"); references.AppendLine("namespace Arch.Core;"); references.AppendComponents(25); references.AppendEntityComponents(25); diff --git a/src/Arch/Arch.csproj b/src/Arch/Arch.csproj index d5a5014..73e2fac 100644 --- a/src/Arch/Arch.csproj +++ b/src/Arch/Arch.csproj @@ -29,8 +29,7 @@ Added World.IsAlive(EntityReference); Fixed bug where `World.TrimExcess` does not trim Recycled-Entities which results in an out of bounds exception sooner or later. Fixed bug in JobScheduler which prevents a deadlock. Moved CommandBuffer to Buffer namespace, might break references. -CommandBuffer now accepts a world during playback, world in ctor was removed. -CommandBuffer now triggers OnComponentRemoved events. +CommandBuffer now accepts a world during playback, world in ctor was removed. c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system;stride;unity;godot; https://github.com/genaray/Arch @@ -103,7 +102,7 @@ CommandBuffer now triggers OnComponentRemoved events. - + diff --git a/src/Arch/Core/Chunk.cs b/src/Arch/Core/Chunk.cs index ce0b262..69ed9d7 100644 --- a/src/Arch/Core/Chunk.cs +++ b/src/Arch/Core/Chunk.cs @@ -256,7 +256,7 @@ public T[] GetArray() { var index = Index(); Debug.Assert(index != -1 && index < Components.Length, $"Index is out of bounds, component {typeof(T)} with id {index} does not exist in this chunk."); - ref var array = ref Components.DangerousGetReferenceAt(index); + var array = Components.DangerousGetReferenceAt(index); return Unsafe.As(array); } @@ -288,7 +288,6 @@ public ref T GetFirst() public partial struct Chunk { - /// /// Sets or replaces a component for an index in the chunk. /// This won't fire an event for . diff --git a/src/Arch/Core/Enumerators.cs b/src/Arch/Core/Enumerators.cs index 3977b87..2b72ef7 100644 --- a/src/Arch/Core/Enumerators.cs +++ b/src/Arch/Core/Enumerators.cs @@ -1,4 +1,5 @@ using Arch.Core.Extensions.Internal; +using Arch.Core.Utils; using CommunityToolkit.HighPerformance; namespace Arch.Core; diff --git a/src/Arch/Core/Utils/ReadOnlyRef.cs b/src/Arch/Core/Utils/ReadOnlyRef.cs new file mode 100644 index 0000000..4557d41 --- /dev/null +++ b/src/Arch/Core/Utils/ReadOnlyRef.cs @@ -0,0 +1,71 @@ +using CommunityToolkit.HighPerformance.Helpers; + +namespace Arch.Core.Utils; + +#if NETSTANDARD2_1_OR_GREATER || NET6_0 + +/// +/// The struct +/// can store a readonly reference to a value of a specified type. +/// +/// The type of value to reference. +public readonly ref struct ReadOnlyRef +{ + /// + /// The 1-length instance used to track the target value. + /// + internal readonly ReadOnlySpan Span; + + /// + /// Initializes a new instance of the struct. + /// + /// The readonly reference to the target value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyRef(in T value) + { + ref T r0 = ref Unsafe.AsRef(value); + + this.Span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The pointer to the target value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ReadOnlyRef(void* pointer) + : this(in Unsafe.AsRef(pointer)) + { + } + + /// + /// Gets the readonly reference represented by the current instance. + /// + public ref readonly T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref MemoryMarshal.GetReference(this.Span); + } + + /// + /// Implicitly converts a instance into a one. + /// + /// The input instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlyRef(Ref reference) + { + return new(in reference.Value); + } + + /// + /// Implicitly gets the value from a given instance. + /// + /// The input instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator T(ReadOnlyRef reference) + { + return reference.Value; + } +} + +#endif diff --git a/src/Arch/Core/Utils/Ref.cs b/src/Arch/Core/Utils/Ref.cs new file mode 100644 index 0000000..02be481 --- /dev/null +++ b/src/Arch/Core/Utils/Ref.cs @@ -0,0 +1,56 @@ +namespace Arch.Core.Utils; + +#if NETSTANDARD2_1_OR_GREATER || NET6_0 + +/// +/// The strct +/// can store a reference to a value of a specified type. +/// +/// The type of value to reference. +public readonly ref struct Ref +{ + /// + /// The 1-length instance used to track the target value. + /// + internal readonly Span Span; + + /// + /// Initializes a new instance of the struct. + /// + /// The reference to the target value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Ref(ref T value) + { + this.Span = MemoryMarshal.CreateSpan(ref value, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The pointer to the target value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe Ref(void* pointer) + : this(ref Unsafe.AsRef(pointer)) + { + } + + /// + /// Gets the reference represented by the current instance. + /// + public ref T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref MemoryMarshal.GetReference(this.Span); + } + + /// + /// Implicitly gets the value from a given instance. + /// + /// The input instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator T(Ref reference) + { + return reference.Value; + } +} +#endif