├── .github └── workflows │ └── main.yml ├── .gitignore ├── Arch.AOT.SourceGenerator ├── Arch.AOT.SourceGenerator.csproj ├── ComponentType.cs ├── Extensions │ └── StringBuilderExtensions.cs └── SourceGenerator.cs ├── Arch.EventBus ├── Arch.EventBus.csproj ├── EventBus.cs ├── Hooks.cs ├── IMethodSymbolExtensions.cs └── SourceGenerator.cs ├── Arch.Extended.Sample ├── Arch.Extended.Sample.csproj ├── Components.cs ├── Extensions.cs ├── Game.cs ├── Program.cs ├── Serializer.cs └── Systems.cs ├── Arch.Extended.sln ├── Arch.Extended.sln.DotSettings ├── Arch.LowLevel.Tests ├── Arch.LowLevel.Tests.csproj ├── ArrayTest.cs ├── Jagged │ └── JaggedArrayTest.cs ├── ResourcesTest.cs ├── UnsafeArrayTest.cs ├── UnsafeListTest.cs ├── UnsafeQueueTest.cs ├── UnsafeStackTest.cs └── Usings.cs ├── Arch.LowLevel ├── Arch.LowLevel.csproj ├── Array.cs ├── Enumerators.cs ├── Jagged │ ├── JaggedArray.cs │ ├── SparseJaggedArray.cs │ ├── UnsafeJaggedArray.cs │ └── UnsafeSparseJaggedArray.cs ├── Resources.cs ├── UnsafeArray.cs ├── UnsafeList.cs ├── UnsafeQueue.cs └── UnsafeStack.cs ├── Arch.Persistence.Tests ├── Arch.Persistence.Tests.csproj ├── PersistenceTest.cs └── Usings.cs ├── Arch.Persistence ├── Arch.Persistence.csproj ├── Binary.cs ├── Json.cs ├── Serializer.cs └── StreamBufferWriter.cs ├── Arch.Relationships.Tests ├── Arch.Relationships.Tests.csproj ├── RelationshipTest.cs └── Usings.cs ├── Arch.Relationships ├── Arch.Relationships.csproj ├── EntityRelationshipExtensions.cs ├── Enumerators.cs ├── InRelationship.cs ├── Relationship.cs └── WorldRelationshipExtensions.cs ├── Arch.System.SourceGenerator.SnapshotTests ├── .editorconfig ├── Arch.System.SourceGenerator.SnapshotTests.csproj └── SnapshotTest.cs ├── Arch.System.SourceGenerator.Tests ├── .editorconfig ├── .gitignore ├── Arch.System.SourceGenerator.Tests.csproj ├── AttributeQueryCompilation │ ├── AttributeQuerySystem.cs │ └── ExpectedGeneration │ │ ├── AttributeQuerySystem.IncrementA(Entity).g.cs │ │ ├── AttributeQuerySystem.IncrementAAndB(Entity).g.cs │ │ ├── AttributeQuerySystem.IncrementAAndBExclusive(Entity).g.cs │ │ ├── AttributeQuerySystem.IncrementANotB(Entity).g.cs │ │ ├── AttributeQuerySystem.IncrementAOrB(Entity).g.cs │ │ └── AttributeQuerySystem.IncrementAOrBNotC(Entity).g.cs ├── BasicCompilation │ ├── BasicSystem.cs │ └── ExpectedGeneration │ │ ├── BasicSystem.Basic(IntComponentA).g.cs │ │ └── BasicSystem.BasicStatic(IntComponentA).g.cs ├── DataParamCompilation │ ├── DataParamSystem.cs │ └── ExpectedGeneration │ │ ├── DataParamSystem.AssignEntityDataParamWithEntityRight(in Entity, in IntComponentA, ref Entity).g.cs │ │ ├── DataParamSystem.CountANoParams(ref int).g.cs │ │ ├── DataParamSystem.CountATwiceWithParams(ref int, in IntComponentA, ref int, in IntComponentB).g.cs │ │ ├── DataParamSystem.CountAWithEntityAndParamLeft(ref int, in IntComponentA, in Entity).g.cs │ │ ├── DataParamSystem.CountAWithEntityAndParamRight(in Entity, in IntComponentA, ref int).g.cs │ │ ├── DataParamSystem.CountAWithEntityLeft(ref int, in Entity).g.cs │ │ ├── DataParamSystem.CountAWithEntityRight(in Entity, ref int).g.cs │ │ ├── DataParamSystem.CountAWithParamsLeft(ref int, in IntComponentA).g.cs │ │ ├── DataParamSystem.CountAWithParamsMiddle(in IntComponentA, ref int, in IntComponentB).g.cs │ │ └── DataParamSystem.CountAWithParamsRight(in IntComponentA, ref int).g.cs ├── GeneratedUpdateCompilation │ ├── ExpectedGeneration │ │ ├── GeneratedUpdateSystem.AutoRunA().g.cs │ │ ├── GeneratedUpdateSystem.AutoRunB().g.cs │ │ └── GeneratedUpdateSystem.g.cs │ └── GeneratedUpdateSystem.cs ├── ParamQueryCompilation │ ├── ExpectedGeneration │ │ ├── ParamQuerySystem.IncrementA(ref IntComponentA).g.cs │ │ ├── ParamQuerySystem.IncrementAAndB(ref IntComponentA, ref IntComponentB).g.cs │ │ ├── ParamQuerySystem.IncrementANotC(ref IntComponentA).g.cs │ │ └── ParamQuerySystem.IncrementOnlyAWithB(ref IntComponentA, in IntComponentB).g.cs │ └── ParamQuerySystem.cs ├── Shared │ ├── BaseTestSystem.cs │ └── IntComponents.cs └── SystemsTest.cs ├── Arch.System.SourceGenerator ├── Arch.System.SourceGenerator.csproj ├── Extensions │ ├── IMethodSymbolExtensions.cs │ └── StringBuilderExtensions.cs ├── Model.cs ├── Query.cs └── SourceGenerator.cs ├── Arch.System ├── Arch.System.csproj ├── Attributes.cs ├── Systems.cs └── Templates │ ├── GenericAttributes.cs │ ├── GenericAttributes.tt │ └── Helpers.ttinclude ├── Directory.Build.targets ├── LICENSE.MD ├── README.md └── scripts └── UnityPublish.sh /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | 8 | jobs: 9 | build: 10 | name: Test 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | build: [linux-debug, linux-release] 16 | include: 17 | - build: linux-debug 18 | os: ubuntu-latest 19 | config: debug 20 | - build: linux-release 21 | os: ubuntu-latest 22 | config: release 23 | steps: 24 | - uses: actions/checkout@v3 25 | - uses: actions/setup-dotnet@v3 26 | with: 27 | dotnet-version: | 28 | 6.0.x 29 | 7.0.x 30 | # workaround for actions/setup-dotnet#155 31 | - name: Clear package cache 32 | run: dotnet clean Arch.Extended.sln && dotnet nuget locals all --clear 33 | - name: Restore packages 34 | run: dotnet restore Arch.Extended.sln 35 | - name: Build 36 | run: dotnet build Arch.Extended.sln -c ${{ matrix.config }} --no-restore 37 | - name: Test 38 | run: dotnet test Arch.Extended.sln -c ${{ matrix.config }} -l "console;verbosity=detailed" 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | bin/ 3 | obj/ 4 | /packages/ 5 | riderModule.iml 6 | /_ReSharper.Caches/ 7 | 8 | # Common IntelliJ Platform excludes 9 | 10 | # User specific 11 | **/.idea/**/workspace.xml 12 | **/.idea/**/tasks.xml 13 | **/.idea/shelf/* 14 | **/.idea/dictionaries 15 | **/.idea/httpRequests/ 16 | 17 | # Sensitive or high-churn files 18 | **/.idea/**/dataSources/ 19 | **/.idea/**/dataSources.ids 20 | **/.idea/**/dataSources.xml 21 | **/.idea/**/dataSources.local.xml 22 | **/.idea/**/sqlDataSources.xml 23 | **/.idea/**/dynamic.xml 24 | 25 | # Rider 26 | # Rider auto-generates .iml files, and contentModel.xml 27 | .idea 28 | **/.idea/**/*.iml 29 | **/.idea/**/contentModel.xml 30 | **/.idea/**/modules.xml 31 | 32 | *.suo 33 | *.user 34 | .vs/ 35 | [Bb]in/ 36 | [Oo]bj/ 37 | _UpgradeReport_Files/ 38 | [Pp]ackages/ 39 | 40 | Thumbs.db 41 | Desktop.ini 42 | .DS_Store 43 | Footer 44 | © 2022 GitHub, Inc. 45 | Footer navigation 46 | Terms 47 | 48 | -------------------------------------------------------------------------------- /Arch.AOT.SourceGenerator/Arch.AOT.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | enable 6 | enable 7 | latest 8 | 9 | true 10 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 11 | true 12 | snupkg 13 | 14 | Arch.AOT.SourceGenerator 15 | Arch.AOT.SourceGenerator 16 | 1.0.1 17 | genaray 18 | Apache-2.0 19 | A source generator for arch to support AOT. 20 | Updated to Arch 1.7 and up. 21 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 22 | 23 | https://github.com/genaray/Arch.Extended 24 | https://github.com/genaray/Arch.Extended.git 25 | git 26 | true 27 | 28 | 11 29 | true 30 | Apache2.0 31 | 32 | en-US 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Arch.AOT.SourceGenerator/ComponentType.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.AOT.SourceGenerator; 2 | 3 | /// 4 | /// The struct 5 | /// represents an Component (Their type with meta data) for use in the generated code. 6 | /// 7 | public struct ComponentType 8 | { 9 | /// 10 | /// The type name of the component. 11 | /// 12 | public string TypeName { get; } 13 | /// 14 | /// If the component has zero fields. 15 | /// 16 | public bool IsZeroSize { get; } 17 | /// 18 | /// If the component is a value type. 19 | /// 20 | public bool IsValueType { get; } 21 | 22 | /// 23 | /// Creates a new instance of the . 24 | /// 25 | /// The type name. 26 | /// If its zero sized. 27 | /// If its a value type. 28 | public ComponentType(string typeName, bool isZeroSize, bool isValueType) 29 | { 30 | TypeName = typeName; 31 | IsZeroSize = isZeroSize; 32 | IsValueType = isValueType; 33 | } 34 | } -------------------------------------------------------------------------------- /Arch.AOT.SourceGenerator/Extensions/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Arch.AOT.SourceGenerator.Extensions; 4 | 5 | /// 6 | /// The class 7 | /// adds code-generating methods to the string-builder for outsourcing them. 8 | /// 9 | public static class StringBuilderExtensions 10 | { 11 | /// 12 | /// Appends the component types to the string builder in the form of a generated class. 13 | /// 14 | /// The target string builder. 15 | /// The types to append. 16 | /// 17 | public static StringBuilder AppendComponentTypes(this StringBuilder sb, IList componentTypes) 18 | { 19 | // Lists the component registration commands line by line. 20 | var components = new StringBuilder(); 21 | foreach (var type in componentTypes) 22 | { 23 | var componentType = type; 24 | components.AppendComponentType(ref componentType); 25 | } 26 | 27 | sb.AppendLine( 28 | $$""" 29 | using System.Runtime.CompilerServices; 30 | using Arch.Core.Utils; 31 | 32 | namespace Arch.AOT.SourceGenerator 33 | { 34 | internal static class GeneratedComponentRegistry 35 | { 36 | [ModuleInitializer] 37 | public static void Initialize() 38 | { 39 | {{components}} 40 | } 41 | } 42 | } 43 | """ 44 | ); 45 | return sb; 46 | } 47 | 48 | /// 49 | /// Appends a single component type to the string builder as a new line. 50 | /// 51 | /// The string builder. 52 | /// The component type to add. 53 | /// 54 | public static StringBuilder AppendComponentType(this StringBuilder sb, ref ComponentType componentType) 55 | { 56 | //var size = componentType.IsValueType ? $"Unsafe.SizeOf<{componentType.TypeName}>()" : "IntPtr.Size"; 57 | //sb.AppendLine($"ComponentRegistry.Add(typeof({componentType.TypeName}), new ComponentType(ComponentRegistry.Size + 1, {size}));"); 58 | 59 | sb.AppendLine($"ArrayRegistry.Add<{componentType.TypeName}>();"); 60 | return sb; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Arch.AOT.SourceGenerator/SourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Text; 3 | using Arch.AOT.SourceGenerator.Extensions; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | using Microsoft.CodeAnalysis.Text; 8 | 9 | namespace Arch.AOT.SourceGenerator; 10 | 11 | /// 12 | /// Incremental generator that generates a class that adds all components to the ComponentRegistry. 13 | /// 14 | [Generator(LanguageNames.CSharp)] 15 | public sealed class ComponentRegistryGenerator : IIncrementalGenerator 16 | { 17 | /// 18 | /// A of annotated components (their types) found via the source-gen. 19 | /// 20 | private readonly List _componentTypes = new(); 21 | 22 | /// 23 | /// The attribute to mark components with in order to be found by this source-gen. 24 | /// 25 | private const string AttributeTemplate = """ 26 | using System; 27 | 28 | namespace Arch.AOT.SourceGenerator 29 | { 30 | [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] 31 | public sealed class ComponentAttribute : Attribute { } 32 | } 33 | """; 34 | public void Initialize(IncrementalGeneratorInitializationContext context) 35 | { 36 | // Register the attribute. 37 | context.RegisterPostInitializationOutput(initializationContext => 38 | { 39 | initializationContext.AddSource("Components.Attributes.g.cs", SourceText.From(AttributeTemplate, Encoding.UTF8)); 40 | }); 41 | 42 | var provider = context.SyntaxProvider.CreateSyntaxProvider( 43 | ShouldTypeBeRegistered, 44 | GetMemberDeclarationsForSourceGen).Where(t => t.attributeFound).Select((t, _) => t.Item1 45 | ); 46 | 47 | context.RegisterSourceOutput( 48 | context.CompilationProvider.Combine(provider.Collect()), (productionContext, tuple) => GenerateCode(productionContext, tuple.Left, tuple.Right) 49 | ); 50 | } 51 | 52 | /// 53 | /// Determines if a node should be considered for code generation. 54 | /// 55 | /// 56 | /// 57 | /// 58 | private static bool ShouldTypeBeRegistered(SyntaxNode node, CancellationToken cancellationToken) 59 | { 60 | if (node is not TypeDeclarationSyntax typeDeclarationSyntax) 61 | { 62 | return false; 63 | } 64 | 65 | return typeDeclarationSyntax.AttributeLists.Count != 0; 66 | } 67 | 68 | /// 69 | /// Make sure the type is annotated with the Component attribute. 70 | /// 71 | /// 72 | /// 73 | /// 74 | private static (TypeDeclarationSyntax, bool attributeFound) GetMemberDeclarationsForSourceGen(GeneratorSyntaxContext context, CancellationToken cancellationToken) 75 | { 76 | var typeDeclarationSyntax = (TypeDeclarationSyntax) context.Node; 77 | 78 | // Stop here if we can't get the type symbol for some reason. 79 | if (ModelExtensions.GetDeclaredSymbol(context.SemanticModel, typeDeclarationSyntax) is not ITypeSymbol symbol) 80 | { 81 | return (typeDeclarationSyntax, false); 82 | } 83 | 84 | // Go through all the attributes. 85 | foreach (var attributeData in symbol.GetAttributes()) 86 | { 87 | if (attributeData.AttributeClass is null) 88 | { 89 | continue; 90 | } 91 | 92 | // If the attribute is the Component attribute, we can stop here and return true. 93 | if (string.Equals(attributeData.AttributeClass.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), "global::Arch.AOT.SourceGenerator.ComponentAttribute", 94 | StringComparison.Ordinal)) 95 | { 96 | return (typeDeclarationSyntax, true); 97 | } 98 | } 99 | 100 | // No attribute found, return false. 101 | return (typeDeclarationSyntax, false); 102 | } 103 | 104 | private void GenerateCode(SourceProductionContext productionContext, Compilation compilation, ImmutableArray typeList) 105 | { 106 | var sb = new StringBuilder(); 107 | _componentTypes.Clear(); 108 | 109 | foreach (var type in typeList) 110 | { 111 | // Get the symbol for the type. 112 | var symbol = ModelExtensions.GetDeclaredSymbol(compilation.GetSemanticModel(type.SyntaxTree), type); 113 | 114 | // If the symbol is not a type symbol, we can't do anything with it. 115 | if (symbol is not ITypeSymbol typeSymbol) 116 | { 117 | continue; 118 | } 119 | 120 | // Check if there are any fields in the type. 121 | var hasZeroFields = true; 122 | foreach (var member in typeSymbol.GetMembers()) 123 | { 124 | if (member is not IFieldSymbol) continue; 125 | 126 | hasZeroFields = false; 127 | break; 128 | } 129 | 130 | var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 131 | _componentTypes.Add(new ComponentType(typeName, hasZeroFields, typeSymbol.IsValueType)); 132 | } 133 | 134 | sb.AppendComponentTypes(_componentTypes); 135 | productionContext.AddSource("GeneratedComponentRegistry.g.cs",CSharpSyntaxTree.ParseText(sb.ToString()).GetRoot().NormalizeWhitespace().ToFullString()); 136 | } 137 | } -------------------------------------------------------------------------------- /Arch.EventBus/Arch.EventBus.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | enable 6 | netstandard2.0 7 | 11 8 | Arch.Bus 9 | 10 | true 11 | snupkg 12 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 13 | 14 | Arch.EventBus 15 | Arch.EventBus 16 | 1.0.2 17 | genaray 18 | Apache-2.0 19 | A basic EventBus using source generation. 20 | Fixed some issues with source generation. 21 | 22 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 23 | 24 | https://github.com/genaray/Arch.Extended 25 | https://github.com/genaray/Arch.Extended.git 26 | git 27 | true 28 | 29 | 11 30 | true 31 | Apache2.0 32 | 33 | en-US 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Arch.EventBus/Hooks.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace Arch.Bus; 5 | 6 | public struct Hooks 7 | { 8 | public List Instances; 9 | } 10 | 11 | public struct ClassHooks 12 | { 13 | public ITypeSymbol PartialClass; 14 | public IList EventHooks; 15 | } 16 | 17 | /// 18 | /// The struct 19 | /// represents a hook for an event from the eventbus inside an class instance. 20 | /// 21 | public struct EventHook 22 | { 23 | 24 | /// 25 | /// The event type. 26 | /// 27 | public ITypeSymbol EventType; 28 | 29 | /// 30 | /// The method symbol of the static event receiver which should be called. 31 | /// 32 | public IMethodSymbol MethodSymbol; 33 | } 34 | 35 | public static class Hookersxtensions 36 | { 37 | 38 | /// 39 | /// Appends add operations for a set of to hook the local class instance into the EventBus instance lists for receiving events. 40 | /// EventBus.SomeClass_SomeEvent_SomeEvent.Add(this); ... 41 | /// 42 | /// The . 43 | /// The of that will be hooked in to receive instance events. 44 | /// 45 | public static StringBuilder Hook(this StringBuilder sb, IList receivingMethods) 46 | { 47 | foreach (var eventReceivingMethod in receivingMethods) 48 | { 49 | var containingSymbol = eventReceivingMethod.MethodSymbol.ContainingSymbol; 50 | var methodName = eventReceivingMethod.MethodSymbol.Name; 51 | 52 | // Remove weird chars to also support value tuples flawlessly, otherwhise they are listed like (World world, int int) in code which destroys everything 53 | var eventType = eventReceivingMethod.EventType.ToString(); 54 | eventType = eventType.Replace("(","").Replace(")","").Replace(".","_").Replace(",","_").Replace(" ",""); 55 | 56 | sb.AppendLine($"EventBus.{containingSymbol.Name}_{methodName}_{eventType}.Add(this);"); 57 | } 58 | return sb; 59 | } 60 | 61 | /// 62 | /// Appends remove operations for a set of to unhook the local class instance from the EventBus instance lists for receiving events. 63 | /// EventBus.SomeClass_SomeEvent_SomeEvent.Remove(this); ... 64 | /// 65 | /// The . 66 | /// The of that will be hooked in to receive instance events. 67 | /// 68 | public static StringBuilder Unhook(this StringBuilder sb, IList receivingMethods) 69 | { 70 | foreach (var eventReceivingMethod in receivingMethods) 71 | { 72 | var containingSymbol = eventReceivingMethod.MethodSymbol.ContainingSymbol; 73 | var methodName = eventReceivingMethod.MethodSymbol.Name; 74 | 75 | // Remove weird chars to also support value tuples flawlessly, otherwhise they are listed like (World world, int int) in code which destroys everything 76 | var eventType = eventReceivingMethod.EventType.ToString(); 77 | eventType = eventType.Replace("(","").Replace(")","").Replace(".","_").Replace(",","_").Replace(" ",""); 78 | 79 | sb.AppendLine($"EventBus.{containingSymbol.Name}_{methodName}_{eventType}.Remove(this);"); 80 | } 81 | return sb; 82 | } 83 | 84 | /// 85 | /// Appends a of and generates proper hook and unhook methods for their partial class instances. 86 | /// 87 | /// The . 88 | /// The of itself, used to generate the hooks in code. 89 | /// 90 | public static StringBuilder AppendHookList(this StringBuilder sb, List hooks) 91 | { 92 | // Loop over all hooks to create the hook and unhook functions. 93 | foreach (var hook in hooks) 94 | { 95 | 96 | var hookIntoEventbus = new StringBuilder().Hook(hook.EventHooks); 97 | var unhookFromEventBus = new StringBuilder().Unhook(hook.EventHooks); 98 | 99 | var template = $$""" 100 | namespace {{hook.PartialClass.ContainingNamespace}}{ 101 | 102 | public partial class {{hook.PartialClass.Name}}{ 103 | 104 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 105 | public void Hook() 106 | { 107 | {{hookIntoEventbus}} 108 | } 109 | 110 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 111 | public void Unhook() 112 | { 113 | {{unhookFromEventBus}} 114 | } 115 | } 116 | } 117 | """; 118 | sb.AppendLine(template); 119 | } 120 | 121 | return sb; 122 | } 123 | 124 | /// 125 | /// Appends a and generates it. 126 | /// 127 | /// The . 128 | /// The itself, used to generate the hooks in code. 129 | /// 130 | public static StringBuilder AppendHooks(this StringBuilder sb, ref Hooks hooks) 131 | { 132 | var callerMethods = new StringBuilder().AppendHookList(hooks.Instances); 133 | var template = $$""" 134 | using System.Runtime.CompilerServices; 135 | using Arch.Bus; 136 | {{callerMethods}} 137 | """; 138 | return sb.Append(template); 139 | } 140 | } -------------------------------------------------------------------------------- /Arch.EventBus/IMethodSymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Arch.Bus; 4 | 5 | public static class IMethodSymbolExtensions 6 | { 7 | 8 | /// 9 | /// Searches attributes of a and returns the first one found. 10 | /// 11 | /// The instance. 12 | /// The attributes name. 13 | /// The attribute wrapped in an . 14 | public static AttributeData GetAttributeData(this IMethodSymbol ms, string name) 15 | { 16 | foreach (var attribute in ms.GetAttributes()) 17 | { 18 | var classSymbol = attribute.AttributeClass; 19 | if(!classSymbol.Name.Contains(name)) continue; 20 | 21 | return attribute; 22 | } 23 | 24 | return default; 25 | } 26 | 27 | /// 28 | /// Gets all the types of a as s and adds them to a list. 29 | /// If the attribute is generic it will add the generic parameters, if its non generic it will add the non generic types from the constructor. 30 | /// 31 | /// The . 32 | /// The where the found s are added to. 33 | public static void GetAttributeTypes(this AttributeData data, List array) 34 | { 35 | if (data is not null && data.AttributeClass.IsGenericType) 36 | { 37 | array.AddRange(data.AttributeClass.TypeArguments); 38 | } 39 | else if (data is not null && !data.AttributeClass.IsGenericType) 40 | { 41 | var constructorArguments = data.ConstructorArguments[0].Values; 42 | var constructorArgumentsTypes = constructorArguments.Select(constant => constant.Value as ITypeSymbol).ToList(); 43 | array.AddRange(constructorArgumentsTypes); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Arch.Extended.Sample/Arch.Extended.Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | Arch.Extended 9 | 12 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Arch.Extended.Sample/Components.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using Arch.AOT.SourceGenerator; 3 | using Arch.Core; 4 | using MessagePack; 5 | using Microsoft.Xna.Framework; 6 | using Microsoft.Xna.Framework.Graphics; 7 | 8 | namespace Arch.Extended; 9 | 10 | /// 11 | /// The position of an entity. 12 | /// 13 | public struct Position 14 | { 15 | 16 | /// 17 | /// Its position. 18 | /// 19 | public Vector2 Vector2; 20 | 21 | /// 22 | /// Constructs a new instance. 23 | /// 24 | /// The x position. 25 | /// The y position. 26 | public Position(float x, float y) 27 | { 28 | Vector2 = new Vector2(x, y); 29 | } 30 | 31 | /// 32 | /// Constructs a new instance. 33 | /// Mostly required for . 34 | /// 35 | /// The , the position. 36 | public Position(Vector2 vector2) 37 | { 38 | Vector2 = vector2; 39 | } 40 | }; 41 | 42 | /// 43 | /// The velocity of an entity. 44 | /// 45 | public struct Velocity 46 | { 47 | 48 | /// 49 | /// Its velocity. 50 | /// 51 | public Vector2 Vector2; 52 | 53 | /// 54 | /// Constructs a new instance. 55 | /// 56 | /// The x velocity. 57 | /// The y velocity. 58 | public Velocity(float x, float y) 59 | { 60 | Vector2 = new Vector2(x, y); 61 | } 62 | 63 | /// 64 | /// Constructs a new instance. 65 | /// Mostly required for . 66 | /// 67 | /// The , the velocity. 68 | public Velocity(Vector2 vector2) 69 | { 70 | Vector2 = vector2; 71 | } 72 | } 73 | 74 | /// 75 | /// The sprite/texture of an entity. 76 | /// 77 | public struct Sprite 78 | { 79 | /// 80 | /// The used. 81 | /// 82 | [IgnoreDataMember] 83 | public Texture2D Texture2D; 84 | 85 | /// 86 | /// The id of the texture, for serialisation. 87 | /// 88 | public byte TextureId; 89 | 90 | /// 91 | /// The used. 92 | /// 93 | public Color Color; 94 | 95 | /// 96 | /// Constructs a new instance. 97 | /// 98 | /// Its . 99 | /// Its . 100 | public Sprite(Texture2D texture2D, Color color) 101 | { 102 | Texture2D = texture2D; 103 | Color = color; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Arch.Extended.Sample/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | 5 | namespace Arch.Extended; 6 | 7 | public static class TextureExtensions 8 | { 9 | /// 10 | /// Creates a square texture and returns it. 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static Texture2D CreateSquareTexture(GraphicsDevice graphicsDevice, int size) 16 | { 17 | var texture = new Texture2D(graphicsDevice, size, size); 18 | var data = new Color[size*size]; 19 | for(var i=0; i < data.Length; ++i) data[i] = Color.White; 20 | texture.SetData(data); 21 | 22 | return texture; 23 | } 24 | } 25 | 26 | public static class RandomExtensions 27 | { 28 | /// 29 | /// Creates a random inside the and returns it. 30 | /// 31 | /// The instance. 32 | /// A in which a is generated. 33 | /// The generated . 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public static Vector2 NextVector2(this Random random, in Rectangle rectangle) 36 | { 37 | return new Vector2(random.Next(rectangle.X, rectangle.X+rectangle.Width), random.Next(rectangle.Y, rectangle.Y+rectangle.Height)); 38 | } 39 | 40 | /// 41 | /// Creates a random between two floats. 42 | /// 43 | /// The instance. 44 | /// The minimum value. 45 | /// The maximum value. 46 | /// A between those to floats. 47 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 48 | public static Vector2 NextVector2(this Random random, float min, float max) 49 | { 50 | return new Vector2((float)(random.NextDouble() * (max - min) + min), (float)(random.NextDouble() * (max - min) + min)); 51 | } 52 | 53 | /// 54 | /// Creates a random . 55 | /// 56 | /// The instance. 57 | /// A . 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static Color NextColor(this Random random) 60 | { 61 | return new Color(random.Next(0,255),random.Next(0,255),random.Next(0,255)); 62 | } 63 | } -------------------------------------------------------------------------------- /Arch.Extended.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | 3 | using Microsoft.Xna.Framework; 4 | using Game = Arch.Extended.Game; 5 | 6 | 7 | // Info : 8 | // This sample demonstrates a example usage of arch. 9 | // Especially a few different iteration techniques for entity iterations. 10 | // Its not a full demonstration of all features. 11 | // Hit "delete" to remove velocity from all entities 12 | 13 | // Disclaimer : 14 | // You can spawn in to 1 million entities, then the performance starts dropping. 15 | // The bottleneck is not the ECS framework, its actually the rendering ( Monogame Spritebatch ). 16 | 17 | Console.WriteLine("Sample App starts..."); 18 | using var game = new Game(); 19 | game.Run(); 20 | 21 | Environment.Exit(0); -------------------------------------------------------------------------------- /Arch.Extended.Sample/Serializer.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using MessagePack.Formatters; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using Utf8Json; 6 | 7 | namespace Arch.Extended; 8 | 9 | /// 10 | /// The class 11 | /// is a for (de)serialising a . 12 | /// 13 | public class SpriteSerializer : IJsonFormatter, IMessagePackFormatter 14 | { 15 | /// 16 | /// The to create s from. 17 | /// 18 | public GraphicsDevice GraphicsDevice { get; set; } 19 | 20 | public void Serialize(ref JsonWriter writer, Sprite value, IJsonFormatterResolver formatterResolver) 21 | { 22 | writer.WriteBeginObject(); 23 | 24 | // Write color 25 | writer.WritePropertyName("color"); 26 | writer.WriteUInt32(value.Color.PackedValue); 27 | writer.WriteValueSeparator(); 28 | 29 | // Write texture id 30 | writer.WritePropertyName("textureId"); 31 | writer.WriteUInt16(value.TextureId); 32 | 33 | writer.WriteEndObject(); 34 | } 35 | 36 | public Sprite Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) 37 | { 38 | reader.ReadIsBeginObject(); 39 | 40 | // Read color 41 | reader.ReadPropertyName(); 42 | var packedColor = reader.ReadUInt32(); 43 | reader.ReadIsValueSeparator(); 44 | 45 | // Read textureid 46 | reader.ReadPropertyName(); 47 | var textureId = reader.ReadUInt16(); 48 | 49 | // Create color and texture 50 | var color = new Color { PackedValue = packedColor }; 51 | var texture = textureId switch 52 | { 53 | 1 => TextureExtensions.CreateSquareTexture(GraphicsDevice, 10), 54 | _ => TextureExtensions.CreateSquareTexture(GraphicsDevice, 10) 55 | }; 56 | 57 | reader.ReadIsEndObject(); 58 | return new Sprite(texture, color); 59 | } 60 | 61 | public void Serialize(ref MessagePackWriter writer, Sprite value, MessagePackSerializerOptions options) 62 | { 63 | // Write color 64 | writer.WriteUInt32(value.Color.PackedValue); 65 | 66 | // Write texture id 67 | writer.WriteUInt16(value.TextureId); 68 | } 69 | 70 | public Sprite Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 71 | { 72 | // Read color 73 | var packedColor = reader.ReadUInt32(); 74 | 75 | // Read textureid 76 | var textureId = reader.ReadUInt16(); 77 | 78 | // Create color and texture 79 | var color = new Color { PackedValue = packedColor }; 80 | var texture = textureId switch 81 | { 82 | 1 => TextureExtensions.CreateSquareTexture(GraphicsDevice, 10), 83 | _ => TextureExtensions.CreateSquareTexture(GraphicsDevice, 10) 84 | }; 85 | 86 | return new Sprite(texture, color); 87 | } 88 | } -------------------------------------------------------------------------------- /Arch.Extended.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34408.163 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.System.SourceGenerator", "Arch.System.SourceGenerator\Arch.System.SourceGenerator.csproj", "{CBF9A4D4-5E31-4457-8F7B-EBC01EB86AF2}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.Extended.Sample", "Arch.Extended.Sample\Arch.Extended.Sample.csproj", "{ED61027D-1586-4349-A6F8-17665C786678}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.System", "Arch.System\Arch.System.csproj", "{39363918-BFC7-43BF-8307-F5581225FB41}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.EventBus", "Arch.EventBus\Arch.EventBus.csproj", "{8BBA34D1-7DAB-4410-8762-09F7CC543D1E}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.LowLevel", "Arch.LowLevel\Arch.LowLevel.csproj", "{00B44305-AB3D-438B-9EB9-8EF1ED2E8394}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.LowLevel.Tests", "Arch.LowLevel.Tests\Arch.LowLevel.Tests.csproj", "{9E2F4FCC-1875-49A6-98F1-1D2DA8460984}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.Persistence", "Arch.Persistence\Arch.Persistence.csproj", "{4E946A7C-5AB3-4AD6-8B80-D27563CF1D6A}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.Relationships", "Arch.Relationships\Arch.Relationships.csproj", "{533453B9-957E-401E-B639-DC81DD0265EC}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.Relationships.Tests", "Arch.Relationships.Tests\Arch.Relationships.Tests.csproj", "{EDAFD1B8-5FC3-4002-B2AD-5B976B809D1C}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.System.SourceGenerator.SnapshotTests", "Arch.System.SourceGenerator.SnapshotTests\Arch.System.SourceGenerator.SnapshotTests.csproj", "{156F6B43-B5F7-48FB-BBDB-85B3968BBE56}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.System.SourceGenerator.Tests", "Arch.System.SourceGenerator.Tests\Arch.System.SourceGenerator.Tests.csproj", "{FDDA22B1-43DF-48E6-82CE-BC528C8349B9}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {CBF9A4D4-5E31-4457-8F7B-EBC01EB86AF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {CBF9A4D4-5E31-4457-8F7B-EBC01EB86AF2}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {CBF9A4D4-5E31-4457-8F7B-EBC01EB86AF2}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {CBF9A4D4-5E31-4457-8F7B-EBC01EB86AF2}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {ED61027D-1586-4349-A6F8-17665C786678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {ED61027D-1586-4349-A6F8-17665C786678}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {ED61027D-1586-4349-A6F8-17665C786678}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {ED61027D-1586-4349-A6F8-17665C786678}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {39363918-BFC7-43BF-8307-F5581225FB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {39363918-BFC7-43BF-8307-F5581225FB41}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {39363918-BFC7-43BF-8307-F5581225FB41}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {39363918-BFC7-43BF-8307-F5581225FB41}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {8BBA34D1-7DAB-4410-8762-09F7CC543D1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {8BBA34D1-7DAB-4410-8762-09F7CC543D1E}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {8BBA34D1-7DAB-4410-8762-09F7CC543D1E}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {8BBA34D1-7DAB-4410-8762-09F7CC543D1E}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {00B44305-AB3D-438B-9EB9-8EF1ED2E8394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {00B44305-AB3D-438B-9EB9-8EF1ED2E8394}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {00B44305-AB3D-438B-9EB9-8EF1ED2E8394}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {00B44305-AB3D-438B-9EB9-8EF1ED2E8394}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {9E2F4FCC-1875-49A6-98F1-1D2DA8460984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {9E2F4FCC-1875-49A6-98F1-1D2DA8460984}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {9E2F4FCC-1875-49A6-98F1-1D2DA8460984}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {9E2F4FCC-1875-49A6-98F1-1D2DA8460984}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {4E946A7C-5AB3-4AD6-8B80-D27563CF1D6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {4E946A7C-5AB3-4AD6-8B80-D27563CF1D6A}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {4E946A7C-5AB3-4AD6-8B80-D27563CF1D6A}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {4E946A7C-5AB3-4AD6-8B80-D27563CF1D6A}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {533453B9-957E-401E-B639-DC81DD0265EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {533453B9-957E-401E-B639-DC81DD0265EC}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {533453B9-957E-401E-B639-DC81DD0265EC}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {533453B9-957E-401E-B639-DC81DD0265EC}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {EDAFD1B8-5FC3-4002-B2AD-5B976B809D1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {EDAFD1B8-5FC3-4002-B2AD-5B976B809D1C}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {EDAFD1B8-5FC3-4002-B2AD-5B976B809D1C}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {EDAFD1B8-5FC3-4002-B2AD-5B976B809D1C}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {156F6B43-B5F7-48FB-BBDB-85B3968BBE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {156F6B43-B5F7-48FB-BBDB-85B3968BBE56}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {156F6B43-B5F7-48FB-BBDB-85B3968BBE56}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {156F6B43-B5F7-48FB-BBDB-85B3968BBE56}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {FDDA22B1-43DF-48E6-82CE-BC528C8349B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {FDDA22B1-43DF-48E6-82CE-BC528C8349B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {FDDA22B1-43DF-48E6-82CE-BC528C8349B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {FDDA22B1-43DF-48E6-82CE-BC528C8349B9}.Release|Any CPU.Build.0 = Release|Any CPU 78 | EndGlobalSection 79 | GlobalSection(SolutionProperties) = preSolution 80 | HideSolutionNode = FALSE 81 | EndGlobalSection 82 | EndGlobal 83 | -------------------------------------------------------------------------------- /Arch.Extended.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | /usr/local/share/dotnet/sdk/8.0.101/MSBuild.dll 3 | 4294967293 -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/Arch.LowLevel.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/ArrayTest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Arch.LowLevel.Tests; 4 | using static Assert; 5 | 6 | /// 7 | /// Checks related methods. 8 | /// 9 | [TestFixture] 10 | public class ArrayTest 11 | { 12 | /// 13 | /// Checks if is capable of allocating space and adding items. 14 | /// 15 | [Test] 16 | public void ArrayCreate() 17 | { 18 | var array = new Array(3); 19 | array[0] = 1; 20 | array[1] = 2; 21 | array[2] = 3; 22 | 23 | That(array.Count, Is.EqualTo(3)); 24 | } 25 | 26 | [Test] 27 | public void ArrayEnumerator() 28 | { 29 | var array = new Array(3); 30 | array[0] = 1; 31 | array[1] = 2; 32 | array[2] = 3; 33 | 34 | var count = 1; 35 | foreach (var item in array) 36 | That(item, Is.EqualTo(count++)); 37 | } 38 | 39 | [Test] 40 | public void ArrayEmptyIsEmpty() 41 | { 42 | var empty = Array.Empty(); 43 | That(empty, Is.Empty); 44 | } 45 | 46 | [Test] 47 | public void ArrayFill() 48 | { 49 | var array = new Array(35); 50 | Array.Fill(ref array, 8); 51 | 52 | for (var i = 0; i < array.Length; i++) 53 | That(array[i], Is.EqualTo(8)); 54 | } 55 | 56 | [Test] 57 | public void ArrayCopy() 58 | { 59 | var src = new Array(15); 60 | var dst = new Array(6); 61 | 62 | for (var i = 0; i < src.Length; i++) 63 | src[i] = i; 64 | 65 | Array.Fill(ref dst); 66 | Array.Copy(ref src, 4, ref dst, 1, 4); 67 | 68 | Multiple(() => 69 | { 70 | That(dst[0], Is.EqualTo(0)); 71 | That(dst[1], Is.EqualTo(4)); 72 | That(dst[2], Is.EqualTo(5)); 73 | That(dst[3], Is.EqualTo(6)); 74 | That(dst[4], Is.EqualTo(7)); 75 | That(dst[5], Is.EqualTo(0)); 76 | }); 77 | } 78 | 79 | [Test] 80 | public void ArrayResizeShrink() 81 | { 82 | var array = new Array(19); 83 | for (var i = 0; i < array.Length; i++) 84 | array[i] = i; 85 | 86 | var resized = Array.Resize(ref array, 8); 87 | for (var i = 0; i < resized.Length; i++) 88 | That(resized[i], Is.EqualTo(i)); 89 | } 90 | 91 | [Test] 92 | public void ArrayResizeGrow() 93 | { 94 | var array = new Array(8); 95 | for (var i = 0; i < array.Length; i++) 96 | array[i] = i; 97 | 98 | var resized = Array.Resize(ref array, 19); 99 | for (var i = 0; i < array.Length; i++) 100 | That(resized[i], Is.EqualTo(i)); 101 | } 102 | 103 | [Test] 104 | public void ArrayEquals() 105 | { 106 | var a = new Array(8); 107 | var b = a; 108 | 109 | That(a, Is.EqualTo(b)); 110 | That(a == b, Is.True); 111 | } 112 | 113 | [Test] 114 | public void ArrayNotEquals() 115 | { 116 | var a = new Array(8); 117 | var b = new Array(8); 118 | 119 | That(a, Is.Not.EqualTo(b)); 120 | That(a != b, Is.True); 121 | } 122 | } -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/Jagged/JaggedArrayTest.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Arch.LowLevel.Jagged; 3 | 4 | namespace Arch.LowLevel.Tests.Jagged; 5 | using static Assert; 6 | 7 | /// 8 | /// Checks related methods. 9 | /// 10 | [TestFixture] 11 | public class JaggedArrayTest 12 | { 13 | 14 | /// 15 | /// Checks if is capable of adding items correctly. 16 | /// 17 | [Test] 18 | public void Add([Values(256,512,1024,2048,4096)] int capacity) 19 | { 20 | // Check add 21 | var jaggedArray = new JaggedArray(16000/Unsafe.SizeOf(), -1, capacity); 22 | 23 | // adding 24 | for (var index = 0; index < jaggedArray.Capacity; index++) 25 | { 26 | jaggedArray.Add(index, index); 27 | } 28 | 29 | // Checking 30 | for (var index = 0; index < jaggedArray.Capacity; index++) 31 | { 32 | var item = jaggedArray[index]; 33 | That(item, Is.EqualTo(index)); 34 | } 35 | 36 | That(jaggedArray.Capacity, Is.GreaterThan(capacity)); 37 | } 38 | 39 | [Test] 40 | public void TryGetValue([Values(256,512,1024,2048,4096)] int capacity) 41 | { 42 | // Initialize the JaggedArray 43 | var jaggedArray = new JaggedArray(16000/Unsafe.SizeOf(), -1, capacity); 44 | 45 | // Add elements to the array 46 | for (var index = 0; index < jaggedArray.Capacity; index++) 47 | { 48 | jaggedArray.Add(index, index); 49 | } 50 | 51 | // Check values using TryGetValue 52 | for (var index = 0; index < jaggedArray.Capacity; index++) 53 | { 54 | var found = jaggedArray.TryGetValue(index, out int value); 55 | That(found, Is.True); 56 | That(value, Is.EqualTo(index)); 57 | } 58 | 59 | // Check for values out of bounds 60 | var outOfBoundsFound = jaggedArray.TryGetValue(jaggedArray.Capacity, out int _); 61 | That(outOfBoundsFound, Is.False); 62 | } 63 | 64 | [Test] 65 | public void TryGetValueRef([Values(256,512,1024,2048,4096)] int capacity) 66 | { 67 | // Initialize the JaggedArray 68 | var jaggedArray = new JaggedArray(16000/Unsafe.SizeOf(), -1, capacity); 69 | 70 | // Add elements to the array 71 | for (var index = 0; index < jaggedArray.Capacity; index++) 72 | { 73 | jaggedArray.Add(index, index); 74 | } 75 | 76 | // Check values using TryGetValue 77 | for (var index = 0; index < jaggedArray.Capacity; index++) 78 | { 79 | bool found; 80 | ref var value = ref jaggedArray.TryGetValue(index, out found); 81 | That(found, Is.True); 82 | That(value, Is.EqualTo(index)); 83 | } 84 | 85 | // Check for values out of bounds 86 | ref var outOfBoundsValue = ref jaggedArray.TryGetValue(jaggedArray.Capacity, out bool outOfBoundsFound); 87 | That(outOfBoundsFound, Is.False); 88 | } 89 | 90 | 91 | /// 92 | /// Checks if is capable of adding items correctly. 93 | /// 94 | [Test] 95 | public void Remove([Values(256,512,1024,2048,4096)] int capacity) 96 | { 97 | // Check add 98 | var jaggedArray = new JaggedArray(16000/Unsafe.SizeOf(), -1, capacity); 99 | 100 | // Adding 101 | for (var index = 0; index < jaggedArray.Capacity; index++) 102 | { 103 | jaggedArray.Add(index, index); 104 | } 105 | 106 | // Removing 107 | for (var index = jaggedArray.Capacity-1; index >= 0; index--) 108 | { 109 | jaggedArray.Remove(index); 110 | } 111 | 112 | // Checking 113 | for (var index = 0; index < jaggedArray.Capacity; index++) 114 | { 115 | var item = jaggedArray[index]; 116 | That(item, Is.EqualTo(-1)); 117 | } 118 | } 119 | 120 | /// 121 | /// Checks if is capable of adding items correctly. 122 | /// 123 | [Test] 124 | public void TrimExcess([Values(2560,5120,10240)] int capacity) 125 | { 126 | // Check add 127 | var jaggedArray = new JaggedArray(16000/Unsafe.SizeOf(), -1, capacity); 128 | 129 | // Adding 130 | for (var index = 0; index < jaggedArray.Capacity; index++) 131 | { 132 | jaggedArray.Add(index, index); 133 | } 134 | 135 | // Removing half of items 136 | for (var index = jaggedArray.Capacity-1; index >= jaggedArray.Capacity/2; index--) 137 | { 138 | jaggedArray.Remove(index); 139 | } 140 | 141 | var buckets = jaggedArray.Buckets; 142 | jaggedArray.TrimExcess(); 143 | That(jaggedArray.Buckets, Is.EqualTo((buckets + 2 - 1)/2)); 144 | 145 | // Checking first half still having the desired value 146 | for (var index = 0; index < jaggedArray.Capacity/2; index++) 147 | { 148 | var item = jaggedArray[index]; 149 | That(item, Is.EqualTo(index)); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/ResourcesTest.cs: -------------------------------------------------------------------------------- 1 | using static NUnit.Framework.Assert; 2 | 3 | namespace Arch.LowLevel.Tests; 4 | 5 | /// 6 | /// Checks and HashCode related methods. 7 | /// 8 | [TestFixture] 9 | public class ResourcesTest 10 | { 11 | 12 | /// 13 | /// Checks if is capable of adding s. 14 | /// 15 | [Test] 16 | public void ResourcesAddHandle() 17 | { 18 | // Check add 19 | var resources = new Resources(IntPtr.Size, capacity: 64); 20 | var handle = resources.Add("Handle"); 21 | var nextHandle = resources.Add("NextHandle"); 22 | 23 | That(handle.Id, Is.EqualTo(0)); 24 | That(nextHandle.Id, Is.EqualTo(1)); 25 | } 26 | 27 | /// 28 | /// Checks if is capable of adding many more s than the capacity 29 | /// 30 | [Test] 31 | public void ResourcesAddManyHandles() 32 | { 33 | const int count = 10000; 34 | 35 | using var resources = new Resources(capacity: 3); 36 | 37 | var handles = new List>(); 38 | for (var i = 0; i < count; i++) 39 | handles.Add(resources.Add(i.ToString())); 40 | 41 | resources.TrimExcess(); 42 | 43 | That(resources.Count, Is.EqualTo(count)); 44 | 45 | for (var i = 0; i < handles.Count; i++) 46 | That(resources.Get(handles[i]), Is.EqualTo(i.ToString())); 47 | } 48 | 49 | /// 50 | /// Checks if is capable of getting s. 51 | /// 52 | [Test] 53 | public void ResourcesGetHandle() 54 | { 55 | // Check add 56 | var resources = new Resources(IntPtr.Size, capacity: 64); 57 | var handle = resources.Add("Handle"); 58 | var nextHandle = resources.Add("NextHandle"); 59 | 60 | // Check get 61 | var handleString = resources.Get(in handle); 62 | var nextHandleString = resources.Get(in nextHandle); 63 | 64 | That(handleString, Is.EqualTo("Handle")); 65 | That(nextHandleString, Is.EqualTo("NextHandle")); 66 | } 67 | 68 | /// 69 | /// Checks if is capable of removing s. 70 | /// 71 | [Test] 72 | public void ResourcesRemoveHandle() 73 | { 74 | 75 | // Check add 76 | var resources = new Resources(IntPtr.Size, capacity: 64); 77 | var handle = resources.Add("Handle"); 78 | var nextHandle = resources.Add("NextHandle"); 79 | 80 | // Check remove 81 | resources.Remove(in handle); 82 | resources.Remove(in nextHandle); 83 | 84 | That(resources._ids.Count, Is.EqualTo(2)); 85 | That(resources.Count, Is.EqualTo(0)); 86 | } 87 | 88 | /// 89 | /// Checks if is capable of removing s. 90 | /// 91 | [Test] 92 | public void ResourcesRecycleHandle() 93 | { 94 | // Check add 95 | var resources = new Resources(IntPtr.Size, capacity: 64); 96 | var handle = resources.Add("Handle"); 97 | var nextHandle = resources.Add("NextHandle"); 98 | 99 | // Check remove 100 | resources.Remove(in handle); 101 | resources.Remove(in nextHandle); 102 | 103 | var newHandle = resources.Add("NewString"); 104 | That(newHandle.Id, Is.EqualTo(0)); 105 | That(resources.Count, Is.EqualTo(1)); 106 | } 107 | 108 | /// 109 | /// Checks if is capable of validating a . 110 | /// 111 | [Test] 112 | public void ResourcesHandleValid() 113 | { 114 | // Check add 115 | var resources = new Resources(IntPtr.Size, capacity: 64); 116 | var handle = resources.Add("Handle"); 117 | Handle someHandle = Handle.NULL; 118 | 119 | That(resources.IsValid(handle), Is.EqualTo(true)); 120 | That(resources.IsValid(someHandle), Is.EqualTo(false)); 121 | } 122 | 123 | /// 124 | /// Checks if throws after Dispose 125 | /// 126 | [Test] 127 | public void ResourcesDispose() 128 | { 129 | // Check add 130 | var resources = new Resources(IntPtr.Size, capacity: 64); 131 | var handle = resources.Add("Handle"); 132 | 133 | // Check get 134 | That(resources.Get(in handle), Is.EqualTo("Handle")); 135 | 136 | resources.Dispose(); 137 | 138 | That(resources.Count, Is.EqualTo(0)); 139 | 140 | // Check that get fails 141 | Throws(() => 142 | { 143 | resources.Get(in handle); 144 | }); 145 | } 146 | } -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/UnsafeArrayTest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Arch.LowLevel.Tests; 4 | using static Assert; 5 | 6 | /// 7 | /// Checks related methods. 8 | /// 9 | [TestFixture] 10 | public class UnsafeArrayTest 11 | { 12 | /// 13 | /// Checks if is capable of allocating space and adding items. 14 | /// 15 | [Test] 16 | public void UnsafeArrayCreate() 17 | { 18 | using var array = new UnsafeArray(3); 19 | array[0] = 1; 20 | array[1] = 2; 21 | array[2] = 3; 22 | 23 | That(array.Count, Is.EqualTo(3)); 24 | } 25 | 26 | [Test] 27 | public void UnsafeArrayEnumerator() 28 | { 29 | using var array = new UnsafeArray(3); 30 | array[0] = 1; 31 | array[1] = 2; 32 | array[2] = 3; 33 | 34 | var count = 1; 35 | foreach (var item in array) 36 | That(item, Is.EqualTo(count++)); 37 | } 38 | 39 | [Test] 40 | public void UnsafeArrayEmptyIsEmpty() 41 | { 42 | var empty = UnsafeArray.Empty(); 43 | 44 | That(empty, Is.Empty); 45 | 46 | empty.Dispose(); 47 | 48 | That(empty, Is.Empty); 49 | } 50 | 51 | [Test] 52 | public void UnsafeArrayFill() 53 | { 54 | var array = new UnsafeArray(35); 55 | using (array) 56 | { 57 | UnsafeArray.Fill(ref array, 8); 58 | 59 | for (var i = 0; i < array.Length; i++) 60 | That(array[i], Is.EqualTo(8)); 61 | } 62 | } 63 | 64 | [Test] 65 | public void UnsafeArrayCopy() 66 | { 67 | var src = new UnsafeArray(15); 68 | var dst = new UnsafeArray(6); 69 | using (src) 70 | using (dst) 71 | { 72 | for (var i = 0; i < src.Length; i++) 73 | src[i] = i; 74 | 75 | UnsafeArray.Fill(ref dst); 76 | UnsafeArray.Copy(ref src, 4, ref dst, 1, 4); 77 | 78 | Multiple(() => 79 | { 80 | That(dst[0], Is.EqualTo(0)); 81 | That(dst[1], Is.EqualTo(4)); 82 | That(dst[2], Is.EqualTo(5)); 83 | That(dst[3], Is.EqualTo(6)); 84 | That(dst[4], Is.EqualTo(7)); 85 | That(dst[5], Is.EqualTo(0)); 86 | }); 87 | } 88 | } 89 | 90 | [Test] 91 | public void UnsafeArrayResizeShrink() 92 | { 93 | var array = new UnsafeArray(19); 94 | for (var i = 0; i < array.Length; i++) 95 | array[i] = i; 96 | 97 | var resized = UnsafeArray.Resize(ref array, 8); 98 | for (var i = 0; i < resized.Length; i++) 99 | That(resized[i], Is.EqualTo(i)); 100 | 101 | resized.Dispose(); 102 | } 103 | 104 | [Test] 105 | public void UnsafeArrayResizeGrow() 106 | { 107 | var array = new UnsafeArray(8); 108 | for (var i = 0; i < array.Length; i++) 109 | array[i] = i; 110 | 111 | var resized = UnsafeArray.Resize(ref array, 19); 112 | for (var i = 0; i < array.Length; i++) 113 | That(resized[i], Is.EqualTo(i)); 114 | 115 | resized.Dispose(); 116 | } 117 | 118 | [Test] 119 | public void UnsafeArrayEquals() 120 | { 121 | using var a = new UnsafeArray(8); 122 | var b = a; 123 | 124 | That(a, Is.EqualTo(b)); 125 | That(a == b, Is.True); 126 | } 127 | 128 | [Test] 129 | public void UnsafeArrayNotEquals() 130 | { 131 | using var a = new UnsafeArray(8); 132 | using var b = new UnsafeArray(8); 133 | 134 | That(a, Is.Not.EqualTo(b)); 135 | That(a != b, Is.True); 136 | } 137 | } -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/UnsafeQueueTest.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.LowLevel.Tests; 2 | using static NUnit.Framework.Assert; 3 | 4 | /// 5 | /// Checks related methods. 6 | /// 7 | [TestFixture] 8 | public class UnsafeQueueTest 9 | { 10 | 11 | /// 12 | /// Checks if is capable of adding items. 13 | /// 14 | [Test] 15 | public void UnsafeQueueEnqueue() 16 | { 17 | using var queue = new UnsafeQueue(8); 18 | 19 | for (var i = 0; i < 20; i++) 20 | queue.Enqueue(i); 21 | 22 | That(queue, Has.Count.EqualTo(20)); 23 | That(queue.Peek(), Is.EqualTo(0)); 24 | } 25 | 26 | /// 27 | /// Checks if is capable of being converted into a span. 28 | /// 29 | [Test] 30 | public void UnsafeQueueAsSpan() 31 | { 32 | using var queue = new UnsafeQueue(8); 33 | 34 | for (var i = 0; i < 9; i++) 35 | queue.Enqueue(i); 36 | 37 | var span = queue.AsSpan(); 38 | 39 | CollectionAssert.AreEqual(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, span.ToArray()); 40 | } 41 | 42 | /// 43 | /// Checks if is capable of peeking itemss. 44 | /// 45 | [Test] 46 | public void UnsafeQueuePeek() 47 | { 48 | using var queue = new UnsafeQueue(8); 49 | queue.Enqueue(1); 50 | queue.Enqueue(2); 51 | 52 | That(queue.Peek(), Is.EqualTo(1)); 53 | queue.Enqueue(3); 54 | That(queue.Peek(), Is.EqualTo(1)); 55 | } 56 | 57 | /// 58 | /// Checks if is capable of popping itemss. 59 | /// 60 | [Test] 61 | public void UnsafeQueueDequeue() 62 | { 63 | using var queue = new UnsafeQueue(8); 64 | queue.Enqueue(1); 65 | queue.Enqueue(2); 66 | queue.Enqueue(3); 67 | 68 | That(queue.Dequeue(), Is.EqualTo(1)); 69 | That(queue.Dequeue(), Is.EqualTo(2)); 70 | That(queue.Dequeue(), Is.EqualTo(3)); 71 | 72 | Throws(() => 73 | { 74 | queue.Dequeue(); 75 | }); 76 | 77 | Throws(() => 78 | { 79 | queue.Peek(); 80 | }); 81 | } 82 | 83 | [Test] 84 | public void UnsafeQueueClear() 85 | { 86 | using var queue = new UnsafeQueue(8); 87 | 88 | for (var i = 0; i < 20; i++) 89 | queue.Enqueue(i); 90 | 91 | That(queue, Has.Count.EqualTo(20)); 92 | 93 | queue.Clear(); 94 | 95 | That(queue, Is.Empty); 96 | } 97 | 98 | /// 99 | /// Checks if is capable of iterating with its enumerators. 100 | /// 101 | [Test] 102 | public void UnsafeQueueEnumerator() 103 | { 104 | using var queue = new UnsafeQueue(8); 105 | queue.Enqueue(1); 106 | queue.Enqueue(2); 107 | queue.Enqueue(3); 108 | 109 | // Ref iterator 110 | var count = 0; 111 | foreach (ref var item in queue) 112 | { 113 | count++; 114 | } 115 | That(count, Is.EqualTo(3)); 116 | } 117 | 118 | /// 119 | /// Checks if can be constructed with invalid parameters. 120 | /// 121 | [Test] 122 | public void UnsafeQueueInvalidConstruction() 123 | { 124 | Throws(() => 125 | { 126 | new UnsafeQueue(-8); 127 | }); 128 | } 129 | 130 | /// 131 | /// Checks if EnsureCapacity functions correctly. 132 | /// 133 | [Test] 134 | public void UnsafeQueueEnsureCapacity() 135 | { 136 | using var queue = new UnsafeQueue(8); 137 | 138 | That(queue.Capacity, Is.AtLeast(8)); 139 | 140 | queue.EnsureCapacity(20); 141 | That(queue.Capacity, Is.AtLeast(20)); 142 | 143 | queue.EnsureCapacity(10); 144 | That(queue.Capacity, Is.AtLeast(20)); 145 | } 146 | 147 | /// 148 | /// Checks if TrimExcess removes all excess capacity. 149 | /// 150 | [Test] 151 | public void UnsafeQueueTrimExcess() 152 | { 153 | using var queue = new UnsafeQueue(8); 154 | for (var i = 0; i < 4; i++) 155 | queue.Enqueue(i); 156 | 157 | That(queue.Capacity, Is.AtLeast(8)); 158 | 159 | queue.EnsureCapacity(20); 160 | That(queue.Capacity, Is.AtLeast(20)); 161 | 162 | queue.TrimExcess(); 163 | 164 | That(queue.Capacity, Is.EqualTo(4)); 165 | } 166 | } -------------------------------------------------------------------------------- /Arch.LowLevel.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /Arch.LowLevel/Arch.LowLevel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0; net6.0; netstandard2.1 5 | enable 6 | enable 7 | true 8 | 9 | true 10 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 11 | true 12 | snupkg 13 | 14 | Arch.LowLevel 15 | Arch.LowLevel 16 | 1.1.5 17 | genaray 18 | Apache-2.0 19 | LowLevel tools for arch. 20 | Refactored JaggedArrays. 21 | Increased performance of JaggedArrays. 22 | Added Array, a new class that acts as a Wrapper around normal generic Arrays for unsafe operations. 23 | Added tests. 24 | Bug fixes. 25 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 26 | 27 | https://github.com/genaray/Arch.Extended 28 | https://github.com/genaray/Arch.Extended.git 29 | git 30 | true 31 | 32 | 11 33 | true 34 | Apache2.0 35 | 36 | en-US 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Arch.LowLevel/Enumerators.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace Arch.LowLevel; 5 | 6 | /// 7 | /// The is a basic implementation of an enumerator for the >. 8 | /// 9 | /// 10 | public unsafe ref struct Enumerator 11 | { 12 | private readonly Span _list; 13 | private readonly int _count; 14 | private int _index; 15 | 16 | /// 17 | /// Creates an instance of the . 18 | /// 19 | /// The . 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | internal Enumerator(Span list) 22 | { 23 | _list = list; 24 | _count = list.Length; 25 | _index = -1; 26 | } 27 | 28 | /// 29 | /// Returns the current item. 30 | /// 31 | public readonly ref T Current => ref _list[_index]; 32 | 33 | /// 34 | /// Moves to the next item. 35 | /// 36 | /// 37 | public bool MoveNext() 38 | { 39 | return unchecked(++_index < _count); 40 | } 41 | 42 | /// 43 | /// Resets the enumerator. 44 | /// 45 | public void Reset() 46 | { 47 | _index = -1; 48 | } 49 | } 50 | 51 | /// 52 | /// The is a basic implementation of the interface for the . 53 | /// 54 | /// 55 | public unsafe struct UnsafeIEnumerator : IEnumerator where T : unmanaged 56 | { 57 | private readonly T* _list; 58 | private readonly int _count; 59 | private int _index; 60 | 61 | /// 62 | /// Creates an instance of the . 63 | /// 64 | /// The . 65 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 66 | internal UnsafeIEnumerator(T* list, int count) 67 | { 68 | _list = list; 69 | _count = count; 70 | _index = -1; 71 | } 72 | 73 | /// 74 | /// Returns the current item. 75 | /// 76 | public T Current => _list[_index]; 77 | 78 | /// 79 | /// Returns the current item. 80 | /// 81 | object IEnumerator.Current => _list[_index]; 82 | 83 | /// 84 | /// Disposes this enumerator. 85 | /// 86 | public void Dispose() { } // nop 87 | 88 | /// 89 | /// Moves to the next item. 90 | /// 91 | /// 92 | public bool MoveNext() 93 | { 94 | return unchecked(++_index < _count); 95 | } 96 | 97 | /// 98 | /// Resets the enumerator. 99 | /// 100 | public void Reset() 101 | { 102 | _index = -1; 103 | } 104 | } 105 | 106 | /// 107 | /// The is a basic implementation of the interface for the . 108 | /// 109 | /// 110 | public unsafe ref struct UnsafeEnumerator where T : unmanaged 111 | { 112 | private readonly T* _list; 113 | private readonly int _count; 114 | private int _index; 115 | 116 | /// 117 | /// Creates an instance of the . 118 | /// 119 | /// The . 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | internal UnsafeEnumerator(T* list, int count) 122 | { 123 | _list = list; 124 | _count = count; 125 | _index = -1; 126 | } 127 | 128 | /// 129 | /// Returns the current item. 130 | /// 131 | public ref T Current => ref _list[_index]; 132 | 133 | /// 134 | /// Moves to the next item. 135 | /// 136 | /// 137 | public bool MoveNext() 138 | { 139 | return unchecked(++_index < _count); 140 | } 141 | 142 | /// 143 | /// Resets the enumerator. 144 | /// 145 | public void Reset() 146 | { 147 | _index = -1; 148 | } 149 | } 150 | 151 | /// 152 | /// The is a basic implementation of the interface for iterating backwards. 153 | /// 154 | /// 155 | public unsafe struct ReverseIEnumerator : IEnumerator where T : unmanaged 156 | { 157 | private readonly T* _list; 158 | private readonly int _count; 159 | private int _index; 160 | 161 | /// 162 | /// Creates an instance of the . 163 | /// 164 | /// The . 165 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 166 | internal ReverseIEnumerator(T* list, int count) 167 | { 168 | _list = list; 169 | _count = count; 170 | _index = count+1; 171 | } 172 | 173 | /// 174 | /// Returns the current item. 175 | /// 176 | public T Current => _list[_index-1]; 177 | 178 | /// 179 | /// Returns the current item. 180 | /// 181 | object IEnumerator.Current => _list[_index-1]; 182 | 183 | /// 184 | /// Disposes this enumerator. 185 | /// 186 | public void Dispose() { } // nop 187 | 188 | /// 189 | /// Moves to the next item. 190 | /// 191 | /// 192 | public bool MoveNext() 193 | { 194 | return unchecked(--_index > 0); 195 | } 196 | 197 | /// 198 | /// Resets the enumerator. 199 | /// 200 | public void Reset() 201 | { 202 | _index = _count+1; 203 | } 204 | } 205 | /// 206 | /// The is a basic implementation of the interface for iterating backwards. 207 | /// 208 | /// 209 | public unsafe ref struct UnsafeReverseEnumerator where T : unmanaged 210 | { 211 | private readonly T* _list; 212 | private readonly int _count; 213 | private int _index; 214 | 215 | /// 216 | /// Creates an instance of the . 217 | /// 218 | /// The . 219 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 220 | internal UnsafeReverseEnumerator(T* list, int count) 221 | { 222 | _list = list; 223 | _count = count; 224 | _index = count+1; 225 | } 226 | 227 | /// 228 | /// Returns the current item. 229 | /// 230 | public ref T Current => ref _list[_index-1]; 231 | 232 | /// 233 | /// Moves to the next item. 234 | /// 235 | /// 236 | public bool MoveNext() 237 | { 238 | return unchecked(--_index > 0); 239 | } 240 | 241 | /// 242 | /// Resets the enumerator. 243 | /// 244 | public void Reset() 245 | { 246 | _index = _count+1; 247 | } 248 | } -------------------------------------------------------------------------------- /Arch.LowLevel/Resources.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Arch.LowLevel.Jagged; 3 | 4 | [assembly: InternalsVisibleTo("Arch.LowLevel.Tests")] 5 | namespace Arch.LowLevel; 6 | 7 | /// 8 | /// The struct 9 | /// represents a reference to an managed resource. 10 | /// This is used commonly for referencing managed resources from components. 11 | /// 12 | /// The type of the managed resource. 13 | public readonly record struct Handle 14 | { 15 | 16 | /// 17 | /// A null which is invalid and used for camparison. 18 | /// 19 | public static readonly Handle NULL = new(-1); 20 | 21 | /// 22 | /// The id, its index inside a array. 23 | /// 24 | public readonly int Id = -1; 25 | 26 | /// 27 | /// Public default constructor. 28 | /// 29 | public Handle() 30 | { 31 | Id = -1; 32 | } 33 | 34 | /// 35 | /// Initializes a new instance of the class. 36 | /// 37 | /// 38 | internal Handle(int id) 39 | { 40 | Id = id; 41 | } 42 | } 43 | 44 | /// 45 | /// The class, 46 | /// represents an collection of managed resources which can be accesed by a . 47 | /// 48 | /// 49 | public sealed class Resources : IDisposable 50 | { 51 | 52 | /// 53 | /// The which stores the managed resources on the index. 54 | /// 55 | private JaggedArray _array; 56 | 57 | /// 58 | /// A list of recycled ids, used to fill in old gaps. 59 | /// 60 | internal Queue _ids; 61 | 62 | /// 63 | /// Creates an instance. 64 | /// 65 | /// The capacity of the bucket. 66 | public Resources(int capacity = 64) 67 | { 68 | _array = new JaggedArray(capacity, capacity); 69 | _ids = new Queue(capacity); 70 | } 71 | 72 | /// 73 | /// Creates an instance. 74 | /// 75 | /// The size of the generic type in bytes. 76 | /// The capcity, how many items of that type should fit into the array. 77 | public Resources(int size, int capacity = 64) 78 | { 79 | _array = new JaggedArray(160000/size, capacity); 80 | _ids = new Queue(capacity); 81 | } 82 | 83 | /// 84 | /// The amount of registered s. 85 | /// 86 | public int Count 87 | { 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | get; 90 | 91 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 92 | private set; 93 | } 94 | 95 | /// 96 | /// Creates a for the given resource. 97 | /// 98 | /// The resource instance. 99 | /// 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public Handle Add(in T item) 102 | { 103 | // Create handle 104 | var recyled = _ids.TryDequeue(out var id); 105 | id = recyled ? id : Count; 106 | var handle = new Handle(id); 107 | 108 | // Resize array and fill it in 109 | _array.EnsureCapacity(id+1); 110 | _array.Add(id, item); 111 | 112 | Count++; 113 | return handle; 114 | } 115 | 116 | /// 117 | /// Checks if the is valid. 118 | /// 119 | /// The . 120 | /// True or false. 121 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 122 | public bool IsValid(in Handle handle) 123 | { 124 | return handle.Id > -1 && handle.Id <= _array.Capacity; 125 | } 126 | 127 | /// 128 | /// Returns a resource for the given . 129 | /// 130 | /// The . 131 | /// The resource. 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public ref T Get(in Handle handle) 134 | { 135 | return ref _array[handle.Id]; 136 | } 137 | 138 | /// 139 | /// Removes a and its resource. 140 | /// 141 | /// The . 142 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 143 | public void Remove(in Handle handle) 144 | { 145 | _array.Remove(handle.Id); 146 | _ids.Enqueue(handle.Id); 147 | 148 | Count--; 149 | } 150 | 151 | /// 152 | /// Trims the resources and releases unused memory if possible. 153 | /// 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | public void TrimExcess() 156 | { 157 | _array.TrimExcess(); 158 | _ids.TrimExcess(); 159 | } 160 | 161 | /// 162 | /// Disposes this instance. 163 | /// 164 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 165 | public void Dispose() 166 | { 167 | _array = null; 168 | _ids = null; 169 | Count = 0; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Arch.Persistence.Tests/Arch.Persistence.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Arch.Persistence.Tests/PersistenceTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Core.Extensions; 3 | using CommunityToolkit.HighPerformance; 4 | using static NUnit.Framework.Assert; 5 | using Throws = NUnit.Framework.Throws; 6 | 7 | namespace Arch.Persistence.Tests; 8 | 9 | /// 10 | /// The struct 11 | /// represents a test component that is being (de)serialized. 12 | /// 13 | public record struct Transform 14 | { 15 | public float X; 16 | public float Y; 17 | } 18 | 19 | /// 20 | /// The struct 21 | /// represents a test component that is being (de)serialized. 22 | /// 23 | public record struct MetaData 24 | { 25 | public string Name; 26 | } 27 | 28 | public class Tests 29 | { 30 | 31 | private ArchBinarySerializer _binarySerializer; 32 | private ArchJsonSerializer _jsonSerializer; 33 | private World _world; 34 | 35 | [SetUp] 36 | public void Setup() 37 | { 38 | _binarySerializer = new ArchBinarySerializer(); 39 | _jsonSerializer = new ArchJsonSerializer(); 40 | 41 | _world = World.Create(); 42 | for (var index = 0; index < 1000; index++) 43 | { 44 | _world.Create(new Transform { X = index, Y = index }, new MetaData{ Name = index.ToString()}); 45 | } 46 | } 47 | 48 | /// 49 | /// Checks if a world is being serialized and deserialized correctly using the . 50 | /// 51 | [Test] 52 | public void BinaryWorldSerialization() 53 | { 54 | var bytes = _binarySerializer.Serialize(_world); 55 | var newWorld = _binarySerializer.Deserialize(bytes); 56 | 57 | // Equal in structure? 58 | That(newWorld.Capacity, Is.EqualTo(_world.Capacity)); 59 | That(newWorld.Size, Is.EqualTo(_world.Size)); 60 | That(newWorld.Archetypes.Count, Is.EqualTo(_world.Archetypes.Count)); 61 | 62 | // Are archetypes equal? 63 | for (var index = 0; index < _world.Archetypes.Count; index++) 64 | { 65 | var archetype = _world.Archetypes[index]; 66 | var newArchetype = newWorld.Archetypes[index]; 67 | 68 | That(archetype.ChunkCapacity, Is.EqualTo(newArchetype.ChunkCapacity)); 69 | That(archetype.EntityCount, Is.EqualTo(newArchetype.EntityCount)); 70 | } 71 | 72 | // Are entities equal? 73 | var entities = new Entity[_world.Size]; 74 | _world.GetEntities(new QueryDescription().WithNone(), entities.AsSpan()); 75 | 76 | var newEntities = new Entity[newWorld.Size]; 77 | newWorld.GetEntities(new QueryDescription(), newEntities.AsSpan()); 78 | 79 | for (var index = 0; index < entities.Length; index++) 80 | { 81 | var entity = entities[index]; 82 | var newEntity = newEntities[index]; 83 | 84 | That(entity.Id, Is.EqualTo(newEntity.Id)); 85 | That(entity.Get(), Is.EqualTo(newEntity.Get())); 86 | That(entity.Get(), Is.EqualTo(newEntity.Get())); 87 | } 88 | } 89 | 90 | /// 91 | /// Checks if an entity is being serialized and deserialized correctly using the . 92 | /// 93 | [Test] 94 | public void BinaryEntitySerialization() 95 | { 96 | var entity = _world.Archetypes[0].GetChunk(0).Entity(0); 97 | var bytes = _binarySerializer.Serialize(_world, entity); 98 | 99 | var newWorld = World.Create(); 100 | var newEntity = _binarySerializer.Deserialize(newWorld, bytes); 101 | 102 | That(newEntity.Get(), Is.EqualTo(entity.Get())); 103 | That(newEntity.Get(), Is.EqualTo(entity.Get())); 104 | } 105 | 106 | /// 107 | /// Checks if a world is being serialized and deserialized correctly using the . 108 | /// 109 | [Test] 110 | public void JsonWorldSerialization() 111 | { 112 | var bytes = _jsonSerializer.Serialize(_world); 113 | var newWorld = _jsonSerializer.Deserialize(bytes); 114 | 115 | // Equal in structure? 116 | That(newWorld.Capacity, Is.EqualTo(_world.Capacity)); 117 | That(newWorld.Size, Is.EqualTo(_world.Size)); 118 | That(newWorld.Archetypes.Count, Is.EqualTo(_world.Archetypes.Count)); 119 | 120 | // Are archetypes equal? 121 | for (var index = 0; index < _world.Archetypes.Count; index++) 122 | { 123 | var archetype = _world.Archetypes[index]; 124 | var newArchetype = newWorld.Archetypes[index]; 125 | 126 | That(archetype.ChunkCapacity, Is.EqualTo(newArchetype.ChunkCapacity)); 127 | That(archetype.EntityCount, Is.EqualTo(newArchetype.EntityCount)); 128 | } 129 | 130 | // Are entities equal? 131 | var entities = new Entity[_world.Size]; 132 | _world.GetEntities(new QueryDescription().WithNone(), entities.AsSpan()); 133 | 134 | var newEntities = new Entity[newWorld.Size]; 135 | newWorld.GetEntities(new QueryDescription(), newEntities.AsSpan()); 136 | 137 | for (var index = 0; index < entities.Length; index++) 138 | { 139 | var entity = entities[index]; 140 | var newEntity = newEntities[index]; 141 | 142 | That(entity.Id, Is.EqualTo(newEntity.Id)); 143 | That(entity.Get(), Is.EqualTo(newEntity.Get())); 144 | That(entity.Get(), Is.EqualTo(newEntity.Get())); 145 | } 146 | } 147 | 148 | 149 | /// 150 | /// Checks if an entity is being serialized and deserialized correctly using the . 151 | /// 152 | [Test] 153 | public void JsonEntitySerialization() 154 | { 155 | var entity = _world.Archetypes[0].GetChunk(0).Entity(0); 156 | var bytes = _jsonSerializer.Serialize(_world, entity); 157 | 158 | var newWorld = World.Create(); 159 | var newEntity = _jsonSerializer.Deserialize(newWorld, bytes); 160 | 161 | That(newEntity.Get(), Is.EqualTo(entity.Get())); 162 | That(newEntity.Get(), Is.EqualTo(entity.Get())); 163 | } 164 | } -------------------------------------------------------------------------------- /Arch.Persistence.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /Arch.Persistence/Arch.Persistence.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | enable 6 | net6.0;net7.0;netstandard2.1 7 | 11 8 | 9 | true 10 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 11 | true 12 | snupkg 13 | 14 | Arch.Persistence 15 | A Persistence-Framework for Arch. 16 | Apache2.0 17 | https://github.com/genaray/Arch.Extended 18 | https://github.com/genaray/Arch.Extended.git 19 | git 20 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 21 | Updated to Arch 2.0.0 22 | 2.0.0 23 | en-US 24 | 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Arch.Persistence/StreamBufferWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | 3 | namespace Arch.Persistence; 4 | 5 | /// 6 | /// The class 7 | /// is a small wrapper around a implementing a . 8 | /// It buffers incoming bytes in an internally stored array and flushes it regulary into the -. 9 | /// 10 | public sealed class StreamBufferWriter : IBufferWriter, IDisposable 11 | { 12 | /// 13 | /// The buffer. 14 | /// 15 | private byte[] _buffer; 16 | 17 | /// 18 | /// The . 19 | /// 20 | private readonly Stream _destination; 21 | 22 | /// 23 | /// If this instance owns the stream. 24 | /// 25 | private readonly bool _ownsStream; 26 | 27 | /// 28 | /// The current position and the amount of total leased bytes. 29 | /// 30 | private int _position, _leased; 31 | 32 | /// 33 | /// Creates a new instance. 34 | /// 35 | /// The . 36 | /// The buffer-size of the . 37 | /// If it owns the stream. 38 | public StreamBufferWriter(Stream destination, int bufferSize = 1024, bool ownsStream = true) 39 | { 40 | const int minBufferSize = 128; 41 | if (bufferSize < minBufferSize) 42 | { 43 | bufferSize = minBufferSize; 44 | } 45 | 46 | _buffer = ArrayPool.Shared.Rent(bufferSize); 47 | _ownsStream = ownsStream; 48 | _destination = destination; 49 | } 50 | 51 | /// 52 | /// Leases an amount of bytes from the . 53 | /// 54 | /// The total amount. 55 | /// The leased amount. 56 | private int Lease(int sizeHint) 57 | { 58 | var available = _buffer.Length - _position; 59 | if (available < sizeHint && _position != 0) 60 | { // try to get more 61 | Flush(); 62 | available = _buffer.Length - _position; 63 | } 64 | 65 | _leased = available; 66 | return available; 67 | } 68 | 69 | /// 70 | /// Flushes the buffered bytes to the . 71 | /// 72 | /// If it also should flush the . 73 | public void Flush(bool flushUnderlyingStream = false) 74 | { 75 | if (_position != 0) 76 | { 77 | _destination.Write(_buffer, 0, _position); 78 | _position = 0; 79 | } 80 | if (flushUnderlyingStream) 81 | { 82 | _destination.Flush(); 83 | } 84 | } 85 | 86 | /// 87 | /// Advances the buffer, notifies this instance that there was something new written into the memory. 88 | /// 89 | /// The amount of bytes written. 90 | /// Throws if we are out of memory. 91 | void IBufferWriter.Advance(int count) 92 | { 93 | if (count > _leased || count < 0) throw new ArgumentOutOfRangeException(nameof(count)); 94 | _position += count; 95 | _leased = 0; 96 | } 97 | 98 | /// 99 | /// Returns a partion of the as a . 100 | /// 101 | /// The total amount. 102 | /// The new instance. 103 | Memory IBufferWriter.GetMemory(int sizeHint) 104 | { 105 | var actual = Lease(sizeHint); 106 | return new Memory(_buffer, _position, actual); 107 | } 108 | 109 | /// 110 | /// Returns a partion of the as a . 111 | /// 112 | /// The total amount. 113 | /// The new instance. 114 | Span IBufferWriter.GetSpan(int sizeHint) 115 | { 116 | var actual = Lease(sizeHint); 117 | return new Span(_buffer, _position, actual); 118 | } 119 | 120 | /// 121 | /// Disposes this instance, flushes and releases all memory. 122 | /// 123 | public void Dispose() 124 | { 125 | Flush(true); 126 | 127 | var tmp = _buffer; 128 | _buffer = null; 129 | ArrayPool.Shared.Return(tmp); 130 | 131 | if (_ownsStream) 132 | { 133 | _destination.Dispose(); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Arch.Relationships.Tests/Arch.Relationships.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | Arch.Relationships.Tests 11 | 12 | 13 | 14 | TRACE; 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Arch.Relationships.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /Arch.Relationships/Arch.Relationships.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | enable 6 | net7.0;net6.0;netstandard2.1 7 | 11 8 | 9 | true 10 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 11 | true 12 | snupkg 13 | 14 | Arch.Relationships 15 | Simple Entity-Relationships for Arch. 16 | Apache2.0 17 | https://github.com/genaray/Arch.Extended 18 | https://github.com/genaray/Arch.Extended.git 19 | git 20 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 21 | Relationships now use less memory and are serializable. 22 | genaray 23 | 1.0.1 24 | en-US 25 | 26 | true 27 | 28 | 29 | 30 | TRACE;EVENTS 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Arch.Relationships/EntityRelationshipExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Contracts; 2 | using System.Runtime.CompilerServices; 3 | using Arch.Core; 4 | 5 | namespace Arch.Relationships; 6 | 7 | #if !PURE_ECS 8 | 9 | /// 10 | /// The class 11 | /// stores several methods to forward relationship methods from the to the . 12 | /// 13 | public static class EntityRelationshipExtensions 14 | { 15 | 16 | /// 17 | /// Adds a new relationship to the . 18 | /// 19 | /// The source of the relationship. 20 | /// The target of the relationship. 21 | /// The relationship type. 22 | /// The relationship instance. 23 | public static void AddRelationship(this in Entity source, Entity target, T relationship = default) 24 | { 25 | var world = World.Worlds[source.WorldId]; 26 | world.AddRelationship(source, target, relationship); 27 | } 28 | 29 | /// 30 | /// Sets a relationship to the by updating its relationship data. 31 | /// 32 | /// The source of the relationship. 33 | /// The target of the relationship. 34 | /// The relationship type. 35 | /// The relationship instance. 36 | public static void SetRelationship(this in Entity source, Entity target, T relationship = default) 37 | { 38 | var world = World.Worlds[source.WorldId]; 39 | world.SetRelationship(source, target, relationship); 40 | } 41 | 42 | /// 43 | /// Checks if an has a certain relationship. 44 | /// 45 | /// The relationship type. 46 | /// The source of the relationship. 47 | /// The target of the relationship. 48 | /// True if it has the desired relationship, otherwise false. 49 | [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] 50 | public static bool HasRelationship(this in Entity source, Entity target) 51 | { 52 | var world = World.Worlds[source.WorldId]; 53 | return world.HasRelationship(source, target); 54 | } 55 | 56 | /// 57 | /// Checks if an has a certain relationship. 58 | /// 59 | /// The relationship type. 60 | /// The source of the relationship. 61 | /// True if it has the desired relationship, otherwise false. 62 | [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] 63 | public static bool HasRelationship(this in Entity source) 64 | { 65 | var world = World.Worlds[source.WorldId]; 66 | return world.HasRelationship(source); 67 | } 68 | 69 | /// 70 | /// Returns a relationship of an . 71 | /// 72 | /// The relationship type. 73 | /// The source of the relationship. 74 | /// The target of the relationship. 75 | /// The relationship. 76 | [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] 77 | public static T GetRelationship(this in Entity source, Entity target) 78 | { 79 | var world = World.Worlds[source.WorldId]; 80 | return world.GetRelationship(source, target); 81 | } 82 | 83 | /// 84 | /// Returns a relationship of an . 85 | /// 86 | /// The relationship type. 87 | /// The source of the relationship. 88 | /// The . 89 | [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] 90 | public static ref Relationship GetRelationships(this in Entity source) 91 | { 92 | var world = World.Worlds[source.WorldId]; 93 | return ref world.GetRelationships(source); 94 | } 95 | 96 | /// 97 | /// Tries to return an s relationship of the specified type. 98 | /// Will copy the relationship if its a struct. 99 | /// 100 | /// The relationship type. 101 | /// The source of the relationship. 102 | /// The target of the relationship. 103 | /// The found relationship. 104 | /// True if it exists, otherwise false. 105 | [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] 106 | public static bool TryGetRelationship(this in Entity source, Entity target, out T relationship) 107 | { 108 | var world = World.Worlds[source.WorldId]; 109 | return world.TryGetRelationship(source, target, out relationship); 110 | } 111 | 112 | /// 113 | /// Removes a relationship from an . 114 | /// 115 | /// The relationship type. 116 | /// The to remove the relationship from. 117 | /// The target of the relationship. 118 | public static void RemoveRelationship(this in Entity source, Entity target) 119 | { 120 | var world = World.Worlds[source.WorldId]; 121 | world.RemoveRelationship(source, target); 122 | } 123 | } 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /Arch.Relationships/Enumerators.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Arch.Core; 5 | using Arch.Core.Extensions.Dangerous; 6 | using CommunityToolkit.HighPerformance; 7 | 8 | namespace Arch.Relationships; 9 | 10 | /// 11 | /// The struct 12 | /// is a enumerator to enumerate a passed in an efficient way. 13 | /// 14 | /// 15 | /// 16 | public struct SortedListEnumerator 17 | { 18 | private SortedList sortedList; 19 | private int currentIndex; 20 | 21 | public SortedListEnumerator(SortedList list) 22 | { 23 | sortedList = list; 24 | currentIndex = -1; 25 | } 26 | 27 | public KeyValuePair Current 28 | { 29 | get 30 | { 31 | if (currentIndex == -1 || currentIndex >= sortedList.Count) 32 | throw new InvalidOperationException(); 33 | 34 | var key = sortedList.Keys[currentIndex]; 35 | var value = sortedList.Values[currentIndex]; 36 | return new KeyValuePair(key, value); 37 | } 38 | } 39 | 40 | public bool MoveNext() 41 | { 42 | currentIndex++; 43 | return currentIndex < sortedList.Count; 44 | } 45 | 46 | public void Reset() 47 | { 48 | currentIndex = -1; 49 | } 50 | } -------------------------------------------------------------------------------- /Arch.Relationships/InRelationship.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using Arch.Core.Utils; 3 | 4 | namespace Arch.Relationships; 5 | 6 | 7 | /// 8 | /// The struct 9 | /// represents a reference to a . 10 | /// It sits on an to indicate in which other s it is involved in. 11 | /// 12 | internal readonly struct InRelationship 13 | { 14 | /// 15 | /// The id of the -Component that this points to. 16 | /// Basically the the is in. 17 | /// TODO: Uhmm... how the heck do we convert the Id back to the ? 18 | /// 19 | public readonly int ComponentTypeId; 20 | 21 | /// 22 | /// Creates a new instance. 23 | /// 24 | /// The that represents the relation. 25 | internal InRelationship(ComponentType targetRelation) 26 | { 27 | ComponentTypeId = targetRelation.Id; 28 | } 29 | 30 | /// 31 | /// Creates a new instance. 32 | /// Mostly for binary serialization. 33 | /// 34 | /// The . 35 | internal InRelationship(int componentTypeId) 36 | { 37 | ComponentTypeId = componentTypeId; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Arch.Relationships/Relationship.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Arch.Core; 3 | 4 | namespace Arch.Relationships; 5 | 6 | /// 7 | /// The interface 8 | /// is an interface that provides all methods required to act as a relationship. 9 | /// 10 | internal interface IRelationship 11 | { 12 | /// 13 | /// The amount of relationships currently in the buffer. 14 | /// 15 | int Count 16 | { 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | get; 19 | } 20 | 21 | /// 22 | /// Removes the buffer as a component from the given world and entity. 23 | /// 24 | /// 25 | /// 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | internal void Destroy(World world, Entity source); 28 | 29 | /// 30 | /// Removes the relationship targeting from this buffer. 31 | /// 32 | /// The in the relationship to remove. 33 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 34 | void Remove(Entity target); 35 | } 36 | 37 | /// 38 | /// A buffer storing relationships of and . 39 | /// 40 | /// The type of the second relationship element. 41 | public class Relationship : IRelationship 42 | { 43 | 44 | /// 45 | /// Its relations. 46 | /// 47 | internal readonly SortedList Elements; 48 | 49 | /// 50 | /// Initializes a new instance of an . 51 | /// 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | internal Relationship() 54 | { 55 | Elements = new SortedList(); 56 | } 57 | 58 | /// 59 | /// Initializes a new instance of an . 60 | /// Mostly for binary serialization. 61 | /// 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | internal Relationship(SortedList elements) 64 | { 65 | Elements = elements; 66 | } 67 | 68 | /// 69 | int IRelationship.Count 70 | { 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | get => Elements.Count; 73 | } 74 | 75 | /// 76 | internal int Count 77 | { 78 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 79 | get => ((IRelationship) this).Count; 80 | } 81 | 82 | /// 83 | /// Adds a relationship to this buffer. 84 | /// 85 | /// The instance of the relationship. 86 | /// The target of the relationship. 87 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 88 | internal void Add(in T relationship, Entity target) 89 | { 90 | Elements.Add(target, relationship); 91 | } 92 | 93 | /// 94 | /// Sets the stored for the given . 95 | /// 96 | /// The . 97 | /// The data to store. 98 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 99 | public void Set(Entity entity, T data = default) 100 | { 101 | Elements[entity] = data; 102 | } 103 | 104 | /// 105 | /// Determines whether the given contains the passed or not. 106 | /// 107 | /// The . 108 | /// True or false. 109 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 110 | public bool Contains(Entity entity) 111 | { 112 | return Elements.ContainsKey(entity); 113 | } 114 | 115 | /// 116 | /// Returns the stored for the given . 117 | /// 118 | /// The . 119 | /// The stored . 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public T Get(Entity entity) 122 | { 123 | return Elements[entity]; 124 | } 125 | 126 | /// 127 | /// Returns the stored for the given . 128 | /// 129 | /// The . 130 | /// The stored . 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public bool TryGetValue(Entity entity, out T value) 133 | { 134 | return Elements.TryGetValue(entity, out value); 135 | } 136 | 137 | /// 138 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 | void IRelationship.Remove(Entity target) 140 | { 141 | Elements.Remove(target); 142 | } 143 | 144 | /// 145 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 146 | internal void Remove(Entity target) 147 | { 148 | ((IRelationship) this).Remove(target); 149 | } 150 | 151 | /// 152 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 153 | void IRelationship.Destroy(World world, Entity source) 154 | { 155 | world.Remove>(source); 156 | } 157 | 158 | /// 159 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 160 | internal void Destroy(World world, Entity source) 161 | { 162 | ((IRelationship) this).Destroy(world, source); 163 | } 164 | 165 | /// 166 | /// Creates a new . 167 | /// 168 | /// The new . 169 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 170 | public SortedListEnumerator GetEnumerator() 171 | { 172 | return new SortedListEnumerator(Elements); 173 | } 174 | }; 175 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.SnapshotTests/Arch.System.SourceGenerator.SnapshotTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | enable 5 | enable 6 | false 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/.gitignore: -------------------------------------------------------------------------------- 1 | Generated/ 2 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/Arch.System.SourceGenerator.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | true 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | Generated 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/AttributeQuerySystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Arch.Core; 4 | using NUnit.Framework; 5 | 6 | namespace Arch.System.SourceGenerator.Tests; 7 | 8 | /// 9 | /// Tests queries with different attribute combinations. 10 | /// 11 | internal partial class AttributeQuerySystem : BaseTestSystem 12 | { 13 | public AttributeQuerySystem(World world) : base(world) { } 14 | 15 | [Query] 16 | [All(typeof(IntComponentA))] 17 | public void IncrementA(Entity e) 18 | { 19 | ref var a = ref World.Get(e); 20 | a.Value++; 21 | } 22 | 23 | [Query] 24 | [Any(typeof(IntComponentA), typeof(IntComponentB))] 25 | public void IncrementAOrB(Entity e) 26 | { 27 | ref var a = ref World.TryGetRef(e, out bool aExists); 28 | ref var b = ref World.TryGetRef(e, out bool bExists); 29 | 30 | if (aExists) 31 | { 32 | a.Value++; 33 | } 34 | 35 | if (bExists) 36 | { 37 | b.Value++; 38 | } 39 | } 40 | 41 | [Query] 42 | [Any(typeof(IntComponentA), typeof(IntComponentB))] 43 | [None(typeof(IntComponentC))] 44 | public void IncrementAOrBNotC(Entity e) 45 | { 46 | ref var a = ref World.TryGetRef(e, out bool aExists); 47 | ref var b = ref World.TryGetRef(e, out bool bExists); 48 | 49 | if (aExists) 50 | { 51 | a.Value++; 52 | } 53 | 54 | if (bExists) 55 | { 56 | b.Value++; 57 | } 58 | } 59 | 60 | [Query] 61 | [All(typeof(IntComponentA), typeof(IntComponentB))] 62 | public void IncrementAAndB(Entity e) 63 | { 64 | ref var a = ref World.Get(e); 65 | a.Value++; 66 | ref var b = ref World.Get(e); 67 | b.Value++; 68 | } 69 | 70 | [Query] 71 | [All(typeof(IntComponentA))] 72 | [None(typeof(IntComponentB))] 73 | public void IncrementANotB(Entity e) 74 | { 75 | ref var a = ref World.Get(e); 76 | a.Value++; 77 | } 78 | 79 | [Query] 80 | [Exclusive(typeof(IntComponentA), typeof(IntComponentB))] 81 | public void IncrementAAndBExclusive(Entity e) 82 | { 83 | ref var a = ref World.Get(e); 84 | a.Value++; 85 | ref var b = ref World.Get(e); 86 | b.Value++; 87 | } 88 | 89 | private (Entity Entity, Dictionary ComponentValues)[] 90 | _expectedComponentValues = Array.Empty<(Entity, Dictionary)>(); 91 | 92 | public override void Setup() 93 | { 94 | _expectedComponentValues = new [] 95 | { 96 | (World.Create(new IntComponentA()), 97 | new Dictionary { { typeof(IntComponentA), 0 } }), 98 | (World.Create(new IntComponentB()), 99 | new Dictionary { { typeof(IntComponentB), 0 } }), 100 | (World.Create(new IntComponentA(), new IntComponentB()), 101 | new Dictionary { { typeof(IntComponentA), 0 }, { typeof(IntComponentB), 0 } }), 102 | (World.Create(new IntComponentA(), new IntComponentB(), new IntComponentC()), 103 | new Dictionary { { typeof(IntComponentA), 0 }, { typeof(IntComponentB), 0 }, { typeof(IntComponentC), 0 } }) 104 | }; 105 | } 106 | 107 | private void TestExpectedValues() 108 | { 109 | foreach (var (e, values) in _expectedComponentValues) 110 | { 111 | foreach (var (type, expectedValue) in values) 112 | { 113 | var component = World.Get(e, type) as IIntComponent; 114 | Assert.That(component, Is.Not.Null); 115 | Assert.That(component.Value, Is.EqualTo(expectedValue)); 116 | } 117 | } 118 | } 119 | 120 | public override void Update(in int t) 121 | { 122 | TestExpectedValues(); 123 | 124 | IncrementAQuery(World); 125 | _expectedComponentValues[0].ComponentValues[typeof(IntComponentA)]++; 126 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 127 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentA)]++; 128 | TestExpectedValues(); 129 | 130 | IncrementAOrBQuery(World); 131 | _expectedComponentValues[0].ComponentValues[typeof(IntComponentA)]++; 132 | _expectedComponentValues[1].ComponentValues[typeof(IntComponentB)]++; 133 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 134 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentB)]++; 135 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentA)]++; 136 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentB)]++; 137 | TestExpectedValues(); 138 | 139 | IncrementAOrBNotCQuery(World); 140 | _expectedComponentValues[0].ComponentValues[typeof(IntComponentA)]++; 141 | _expectedComponentValues[1].ComponentValues[typeof(IntComponentB)]++; 142 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 143 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentB)]++; 144 | TestExpectedValues(); 145 | 146 | IncrementAAndBQuery(World); 147 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 148 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentB)]++; 149 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentA)]++; 150 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentB)]++; 151 | TestExpectedValues(); 152 | 153 | IncrementANotBQuery(World); 154 | _expectedComponentValues[0].ComponentValues[typeof(IntComponentA)]++; 155 | TestExpectedValues(); 156 | 157 | IncrementAAndBExclusiveQuery(World); 158 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 159 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentB)]++; 160 | TestExpectedValues(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/ExpectedGeneration/AttributeQuerySystem.IncrementA(Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class AttributeQuerySystem 14 | { 15 | private QueryDescription IncrementA_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private World? _IncrementA_Initialized; 17 | private Query? _IncrementA_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void IncrementAQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementA_Initialized, world)) 22 | { 23 | _IncrementA_Query = world.Query(in IncrementA_QueryDescription); 24 | _IncrementA_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementA_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | IncrementA(@e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/ExpectedGeneration/AttributeQuerySystem.IncrementAAndB(Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class AttributeQuerySystem 14 | { 15 | private QueryDescription IncrementAAndB_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private World? _IncrementAAndB_Initialized; 17 | private Query? _IncrementAAndB_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void IncrementAAndBQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementAAndB_Initialized, world)) 22 | { 23 | _IncrementAAndB_Query = world.Query(in IncrementAAndB_QueryDescription); 24 | _IncrementAAndB_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementAAndB_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | IncrementAAndB(@e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/ExpectedGeneration/AttributeQuerySystem.IncrementAAndBExclusive(Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class AttributeQuerySystem 14 | { 15 | private QueryDescription IncrementAAndBExclusive_QueryDescription = new QueryDescription(all: Signature.Null, any: Signature.Null, none: Signature.Null, exclusive: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB))); 16 | private World? _IncrementAAndBExclusive_Initialized; 17 | private Query? _IncrementAAndBExclusive_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void IncrementAAndBExclusiveQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementAAndBExclusive_Initialized, world)) 22 | { 23 | _IncrementAAndBExclusive_Query = world.Query(in IncrementAAndBExclusive_QueryDescription); 24 | _IncrementAAndBExclusive_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementAAndBExclusive_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | IncrementAAndBExclusive(@e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/ExpectedGeneration/AttributeQuerySystem.IncrementANotB(Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class AttributeQuerySystem 14 | { 15 | private QueryDescription IncrementANotB_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), exclusive: Signature.Null); 16 | private World? _IncrementANotB_Initialized; 17 | private Query? _IncrementANotB_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void IncrementANotBQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementANotB_Initialized, world)) 22 | { 23 | _IncrementANotB_Query = world.Query(in IncrementANotB_QueryDescription); 24 | _IncrementANotB_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementANotB_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | IncrementANotB(@e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/ExpectedGeneration/AttributeQuerySystem.IncrementAOrB(Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class AttributeQuerySystem 14 | { 15 | private QueryDescription IncrementAOrB_QueryDescription = new QueryDescription(all: Signature.Null, any: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), none: Signature.Null, exclusive: Signature.Null); 16 | private World? _IncrementAOrB_Initialized; 17 | private Query? _IncrementAOrB_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void IncrementAOrBQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementAOrB_Initialized, world)) 22 | { 23 | _IncrementAOrB_Query = world.Query(in IncrementAOrB_QueryDescription); 24 | _IncrementAOrB_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementAOrB_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | IncrementAOrB(@e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/AttributeQueryCompilation/ExpectedGeneration/AttributeQuerySystem.IncrementAOrBNotC(Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class AttributeQuerySystem 14 | { 15 | private QueryDescription IncrementAOrBNotC_QueryDescription = new QueryDescription(all: Signature.Null, any: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), none: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentC)), exclusive: Signature.Null); 16 | private World? _IncrementAOrBNotC_Initialized; 17 | private Query? _IncrementAOrBNotC_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void IncrementAOrBNotCQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementAOrBNotC_Initialized, world)) 22 | { 23 | _IncrementAOrBNotC_Query = world.Query(in IncrementAOrBNotC_QueryDescription); 24 | _IncrementAOrBNotC_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementAOrBNotC_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | IncrementAOrBNotC(@e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/BasicCompilation/BasicSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using NUnit.Framework; 3 | 4 | namespace Arch.System.SourceGenerator.Tests; 5 | 6 | /// 7 | /// Tests basic query functionality. 8 | /// 9 | internal partial class BasicSystem : BaseTestSystem 10 | { 11 | public BasicSystem(World world) : base(world) { } 12 | 13 | private int _number = 0; 14 | static private int _numberStatic = 0; 15 | 16 | [Query] 17 | public void Basic(IntComponentA _) 18 | { 19 | _number++; 20 | _numberStatic++; 21 | } 22 | 23 | [Query] 24 | public static void BasicStatic(IntComponentA _) 25 | { 26 | _numberStatic++; 27 | } 28 | 29 | public override void Setup() 30 | { 31 | World.Create(new IntComponentA()); 32 | } 33 | 34 | public override void Update(in int t) 35 | { 36 | Assert.That(_number, Is.EqualTo(0)); 37 | Assert.That(_numberStatic, Is.EqualTo(0)); 38 | BasicQuery(World); 39 | Assert.That(_number, Is.EqualTo(1)); 40 | Assert.That(_numberStatic, Is.EqualTo(1)); 41 | BasicStaticQuery(World); 42 | Assert.That(_number, Is.EqualTo(1)); 43 | Assert.That(_numberStatic, Is.EqualTo(2)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/BasicCompilation/ExpectedGeneration/BasicSystem.Basic(IntComponentA).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class BasicSystem 14 | { 15 | private QueryDescription Basic_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private World? _Basic_Initialized; 17 | private Query? _Basic_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void BasicQuery(World world) 20 | { 21 | if (!ReferenceEquals(_Basic_Initialized, world)) 22 | { 23 | _Basic_Query = world.Query(in Basic_QueryDescription); 24 | _Basic_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _Basic_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref var @_ = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 33 | Basic(@_); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/BasicCompilation/ExpectedGeneration/BasicSystem.BasicStatic(IntComponentA).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class BasicSystem 14 | { 15 | private static QueryDescription BasicStatic_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _BasicStatic_Initialized; 17 | private static Query? _BasicStatic_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void BasicStaticQuery(World world) 20 | { 21 | if (!ReferenceEquals(_BasicStatic_Initialized, world)) 22 | { 23 | _BasicStatic_Query = world.Query(in BasicStatic_QueryDescription); 24 | _BasicStatic_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _BasicStatic_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref var @_ = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 33 | BasicStatic(@_); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/DataParamSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using Arch.Core; 3 | using NUnit.Framework; 4 | 5 | namespace Arch.System.SourceGenerator.Tests; 6 | 7 | /// 8 | /// Tests queries using data parameters. 9 | /// 10 | [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Allow unused params for query headers")] 11 | internal partial class DataParamSystem : BaseTestSystem 12 | { 13 | public DataParamSystem(World world) : base(world) { } 14 | 15 | [Query] 16 | [All(typeof(IntComponentA))] 17 | public static void CountANoParams([Data] ref int count) 18 | { 19 | count++; 20 | } 21 | 22 | [Query] 23 | public static void CountAWithParamsLeft([Data] ref int count, in IntComponentA _) 24 | { 25 | count++; 26 | } 27 | 28 | [Query] 29 | public static void CountAWithParamsRight(in IntComponentA _, [Data] ref int count) 30 | { 31 | count++; 32 | } 33 | 34 | [Query] 35 | public static void CountAWithParamsMiddle(in IntComponentA _, [Data] ref int count, in IntComponentB __) 36 | { 37 | count++; 38 | } 39 | 40 | [Query] 41 | public static void CountATwiceWithParams([Data] ref int count1, in IntComponentA _, [Data] ref int count2, in IntComponentB __) 42 | { 43 | count1++; 44 | count2++; 45 | } 46 | 47 | [Query] 48 | [All(typeof(IntComponentA))] 49 | public static void CountAWithEntityRight(in Entity e, [Data] ref int count) 50 | { 51 | count++; 52 | } 53 | 54 | [Query] 55 | [All(typeof(IntComponentA))] 56 | public static void CountAWithEntityLeft([Data] ref int count, in Entity e) 57 | { 58 | count++; 59 | } 60 | 61 | [Query] 62 | public static void CountAWithEntityAndParamLeft([Data] ref int count, in IntComponentA a, in Entity e) 63 | { 64 | count++; 65 | } 66 | 67 | [Query] 68 | public static void CountAWithEntityAndParamRight(in Entity e, in IntComponentA a, [Data] ref int count) 69 | { 70 | count++; 71 | } 72 | 73 | // compilation fails from https://github.com/genaray/Arch.Extended/issues/89 74 | //[Query] 75 | //public void AssignEntityDataParamRight(in IntComponentA a, [Data] ref Entity outEntity) 76 | //{ 77 | // outEntity = _sampleEntity; 78 | //} 79 | 80 | // compilation fails from https://github.com/genaray/Arch.Extended/issues/89 81 | //[Query] 82 | //public void AssignEntityDataParamLeft([Data] ref Entity outEntity, in IntComponentA a) 83 | //{ 84 | // outEntity = _sampleEntity; 85 | //} 86 | 87 | [Query] 88 | public static void AssignEntityDataParamWithEntityRight(in Entity e, in IntComponentA a, [Data] ref Entity outEntity) 89 | { 90 | outEntity = e; 91 | } 92 | 93 | // compilation fails from https://github.com/genaray/Arch.Extended/issues/89 94 | //[Query] 95 | //public static void AssignEntityDataParamWithEntityLeft([Data] ref Entity outEntity, in Entity e, in IntComponentA a) 96 | //{ 97 | // outEntity = e; 98 | //} 99 | 100 | // Crashes source generator due to ? in filename; see https://github.com/genaray/Arch.Extended/issues/91 101 | //[Query] 102 | //[All(typeof(IntComponentA))] 103 | //public static void CountANullable([Data] ref int? count) 104 | //{ 105 | // count ??= 0; 106 | // count++; 107 | //} 108 | 109 | private Entity _sampleEntity; 110 | public override void Setup() 111 | { 112 | _sampleEntity = World.Create(new IntComponentA(), new IntComponentB()); 113 | World.Create(new IntComponentA(), new IntComponentB()); 114 | } 115 | 116 | public override void Update(in int t) 117 | { 118 | int i = 0; 119 | CountANoParamsQuery(World, ref i); 120 | Assert.That(i, Is.EqualTo(2)); 121 | 122 | i = 0; 123 | CountAWithParamsLeftQuery(World, ref i); 124 | Assert.That(i, Is.EqualTo(2)); 125 | 126 | i = 0; 127 | CountAWithParamsRightQuery(World, ref i); 128 | Assert.That(i, Is.EqualTo(2)); 129 | 130 | i = 0; 131 | CountAWithParamsMiddleQuery(World, ref i); 132 | Assert.That(i, Is.EqualTo(2)); 133 | 134 | i = 0; 135 | int i2 = 0; 136 | CountATwiceWithParamsQuery(World, ref i, ref i2); 137 | Assert.Multiple(() => 138 | { 139 | Assert.That(i, Is.EqualTo(2)); 140 | Assert.That(i2, Is.EqualTo(2)); 141 | }); 142 | 143 | i = 0; 144 | CountAWithEntityRightQuery(World, ref i); 145 | Assert.That(i, Is.EqualTo(2)); 146 | 147 | i = 0; 148 | CountAWithEntityLeftQuery(World, ref i); 149 | Assert.That(i, Is.EqualTo(2)); 150 | 151 | i = 0; 152 | CountAWithEntityAndParamLeftQuery(World, ref i); 153 | Assert.That(i, Is.EqualTo(2)); 154 | 155 | i = 0; 156 | CountAWithEntityAndParamRightQuery(World, ref i); 157 | Assert.That(i, Is.EqualTo(2)); 158 | 159 | Entity outEntity = Entity.Null; 160 | //AssignEntityDataParamRightQuery(World, ref outEntity); 161 | //Assert.That(outEntity, Is.EqualTo(_sampleEntity)); 162 | 163 | //outEntity = Entity.Null; 164 | //AssignEntityDataParamLeftQuery(World, ref outEntity); 165 | //Assert.That(outEntity, Is.EqualTo(_sampleEntity)); 166 | 167 | outEntity = Entity.Null; 168 | AssignEntityDataParamWithEntityRightQuery(World, ref outEntity); 169 | Assert.That(outEntity, Is.Not.EqualTo(Entity.Null)); 170 | 171 | //outEntity = Entity.Null; 172 | //AssignEntityDataParamWithEntityLeftQuery(World, ref outEntity); 173 | //Assert.That(outEntity, Is.Not.EqualTo(Entity.Null)); 174 | 175 | //int? i3 = null; 176 | //CountANullableQuery(World, ref i3); 177 | //Assert.That(i, Is.EqualTo(2)); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.AssignEntityDataParamWithEntityRight(in Entity, in IntComponentA, ref Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription AssignEntityDataParamWithEntityRight_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _AssignEntityDataParamWithEntityRight_Initialized; 17 | private static Query? _AssignEntityDataParamWithEntityRight_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void AssignEntityDataParamWithEntityRightQuery(World world, ref Arch.Core.Entity @outentity) 20 | { 21 | if (!ReferenceEquals(_AssignEntityDataParamWithEntityRight_Initialized, world)) 22 | { 23 | _AssignEntityDataParamWithEntityRight_Query = world.Query(in AssignEntityDataParamWithEntityRight_QueryDescription); 24 | _AssignEntityDataParamWithEntityRight_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _AssignEntityDataParamWithEntityRight_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 34 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 35 | AssignEntityDataParamWithEntityRight(in @e, in @a, ref @outentity); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountANoParams(ref int).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountANoParams_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountANoParams_Initialized; 17 | private static Query? _CountANoParams_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountANoParamsQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountANoParams_Initialized, world)) 22 | { 23 | _CountANoParams_Query = world.Query(in CountANoParams_QueryDescription); 24 | _CountANoParams_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountANoParams_Query) 28 | { 29 | foreach (var entityIndex in chunk) 30 | { 31 | CountANoParams(ref @count); 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountATwiceWithParams(ref int, in IntComponentA, ref int, in IntComponentB).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountATwiceWithParams_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountATwiceWithParams_Initialized; 17 | private static Query? _CountATwiceWithParams_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountATwiceWithParamsQuery(World world, ref int @count1, ref int @count2) 20 | { 21 | if (!ReferenceEquals(_CountATwiceWithParams_Initialized, world)) 22 | { 23 | _CountATwiceWithParams_Query = world.Query(in CountATwiceWithParams_QueryDescription); 24 | _CountATwiceWithParams_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountATwiceWithParams_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | ref var @intcomponentbFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref var @_ = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 34 | ref var @__ = ref Unsafe.Add(ref intcomponentbFirstElement, entityIndex); 35 | CountATwiceWithParams(ref @count1, in @_, ref @count2, in @__); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithEntityAndParamLeft(ref int, in IntComponentA, in Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithEntityAndParamLeft_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithEntityAndParamLeft_Initialized; 17 | private static Query? _CountAWithEntityAndParamLeft_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithEntityAndParamLeftQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithEntityAndParamLeft_Initialized, world)) 22 | { 23 | _CountAWithEntityAndParamLeft_Query = world.Query(in CountAWithEntityAndParamLeft_QueryDescription); 24 | _CountAWithEntityAndParamLeft_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithEntityAndParamLeft_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 34 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 35 | CountAWithEntityAndParamLeft(ref @count, in @a, in @e); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithEntityAndParamRight(in Entity, in IntComponentA, ref int).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithEntityAndParamRight_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithEntityAndParamRight_Initialized; 17 | private static Query? _CountAWithEntityAndParamRight_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithEntityAndParamRightQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithEntityAndParamRight_Initialized, world)) 22 | { 23 | _CountAWithEntityAndParamRight_Query = world.Query(in CountAWithEntityAndParamRight_QueryDescription); 24 | _CountAWithEntityAndParamRight_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithEntityAndParamRight_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 34 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 35 | CountAWithEntityAndParamRight(in @e, in @a, ref @count); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithEntityLeft(ref int, in Entity).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithEntityLeft_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithEntityLeft_Initialized; 17 | private static Query? _CountAWithEntityLeft_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithEntityLeftQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithEntityLeft_Initialized, world)) 22 | { 23 | _CountAWithEntityLeft_Query = world.Query(in CountAWithEntityLeft_QueryDescription); 24 | _CountAWithEntityLeft_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithEntityLeft_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | CountAWithEntityLeft(ref @count, in @e); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithEntityRight(in Entity, ref int).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithEntityRight_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithEntityRight_Initialized; 17 | private static Query? _CountAWithEntityRight_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithEntityRightQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithEntityRight_Initialized, world)) 22 | { 23 | _CountAWithEntityRight_Query = world.Query(in CountAWithEntityRight_QueryDescription); 24 | _CountAWithEntityRight_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithEntityRight_Query) 28 | { 29 | ref var entityFirstElement = ref chunk.Entity(0); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref readonly var e = ref Unsafe.Add(ref entityFirstElement, entityIndex); 33 | CountAWithEntityRight(in @e, ref @count); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithParamsLeft(ref int, in IntComponentA).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithParamsLeft_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithParamsLeft_Initialized; 17 | private static Query? _CountAWithParamsLeft_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithParamsLeftQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithParamsLeft_Initialized, world)) 22 | { 23 | _CountAWithParamsLeft_Query = world.Query(in CountAWithParamsLeft_QueryDescription); 24 | _CountAWithParamsLeft_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithParamsLeft_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref var @_ = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 33 | CountAWithParamsLeft(ref @count, in @_); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithParamsMiddle(in IntComponentA, ref int, in IntComponentB).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithParamsMiddle_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithParamsMiddle_Initialized; 17 | private static Query? _CountAWithParamsMiddle_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithParamsMiddleQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithParamsMiddle_Initialized, world)) 22 | { 23 | _CountAWithParamsMiddle_Query = world.Query(in CountAWithParamsMiddle_QueryDescription); 24 | _CountAWithParamsMiddle_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithParamsMiddle_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | ref var @intcomponentbFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref var @_ = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 34 | ref var @__ = ref Unsafe.Add(ref intcomponentbFirstElement, entityIndex); 35 | CountAWithParamsMiddle(in @_, ref @count, in @__); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/DataParamCompilation/ExpectedGeneration/DataParamSystem.CountAWithParamsRight(in IntComponentA, ref int).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class DataParamSystem 14 | { 15 | private static QueryDescription CountAWithParamsRight_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _CountAWithParamsRight_Initialized; 17 | private static Query? _CountAWithParamsRight_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void CountAWithParamsRightQuery(World world, ref int @count) 20 | { 21 | if (!ReferenceEquals(_CountAWithParamsRight_Initialized, world)) 22 | { 23 | _CountAWithParamsRight_Query = world.Query(in CountAWithParamsRight_QueryDescription); 24 | _CountAWithParamsRight_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _CountAWithParamsRight_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref var @_ = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 33 | CountAWithParamsRight(in @_, ref @count); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/GeneratedUpdateCompilation/ExpectedGeneration/GeneratedUpdateSystem.AutoRunA().g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class GeneratedUpdateSystem 14 | { 15 | private QueryDescription AutoRunA_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private World? _AutoRunA_Initialized; 17 | private Query? _AutoRunA_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void AutoRunAQuery(World world) 20 | { 21 | if (!ReferenceEquals(_AutoRunA_Initialized, world)) 22 | { 23 | _AutoRunA_Query = world.Query(in AutoRunA_QueryDescription); 24 | _AutoRunA_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _AutoRunA_Query) 28 | { 29 | foreach (var entityIndex in chunk) 30 | { 31 | AutoRunA(); 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/GeneratedUpdateCompilation/ExpectedGeneration/GeneratedUpdateSystem.AutoRunB().g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class GeneratedUpdateSystem 14 | { 15 | private QueryDescription AutoRunB_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private World? _AutoRunB_Initialized; 17 | private Query? _AutoRunB_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public void AutoRunBQuery(World world) 20 | { 21 | if (!ReferenceEquals(_AutoRunB_Initialized, world)) 22 | { 23 | _AutoRunB_Query = world.Query(in AutoRunB_QueryDescription); 24 | _AutoRunB_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _AutoRunB_Query) 28 | { 29 | foreach (var entityIndex in chunk) 30 | { 31 | AutoRunB(); 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/GeneratedUpdateCompilation/ExpectedGeneration/GeneratedUpdateSystem.g.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | using System; 4 | 5 | namespace Arch.System.SourceGenerator.Tests 6 | { 7 | partial class GeneratedUpdateSystem 8 | { 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | public override void Update(in int data) 11 | { 12 | AutoRunAQuery(World); 13 | AutoRunBQuery(World); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/GeneratedUpdateCompilation/GeneratedUpdateSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | using NUnit.Framework; 3 | 4 | namespace Arch.System.SourceGenerator.Tests; 5 | 6 | /// 7 | /// Tests the auto-generated method. 8 | /// 9 | internal partial class GeneratedUpdateSystem : BaseTestSystem 10 | { 11 | public GeneratedUpdateSystem(World world) : base(world) { } 12 | 13 | private int _number = 0; 14 | 15 | [Query] 16 | [All(typeof(IntComponentA))] 17 | public void AutoRunA() 18 | { 19 | Assert.That(_number, Is.EqualTo(0)); 20 | _number++; 21 | } 22 | 23 | [Query] 24 | [All(typeof(IntComponentA))] 25 | public void AutoRunB() 26 | { 27 | Assert.That(_number, Is.EqualTo(1)); 28 | _number++; 29 | } 30 | 31 | public override void Setup() 32 | { 33 | World.Create(new IntComponentA()); 34 | } 35 | 36 | public override void Test() 37 | { 38 | base.Test(); 39 | Assert.That(_number, Is.EqualTo(2)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/ParamQueryCompilation/ExpectedGeneration/ParamQuerySystem.IncrementA(ref IntComponentA).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class ParamQuerySystem 14 | { 15 | private static QueryDescription IncrementA_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _IncrementA_Initialized; 17 | private static Query? _IncrementA_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void IncrementAQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementA_Initialized, world)) 22 | { 23 | _IncrementA_Query = world.Query(in IncrementA_QueryDescription); 24 | _IncrementA_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementA_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 33 | IncrementA(ref @a); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/ParamQueryCompilation/ExpectedGeneration/ParamQuerySystem.IncrementAAndB(ref IntComponentA, ref IntComponentB).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class ParamQuerySystem 14 | { 15 | private static QueryDescription IncrementAAndB_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _IncrementAAndB_Initialized; 17 | private static Query? _IncrementAAndB_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void IncrementAAndBQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementAAndB_Initialized, world)) 22 | { 23 | _IncrementAAndB_Query = world.Query(in IncrementAAndB_QueryDescription); 24 | _IncrementAAndB_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementAAndB_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | ref var @intcomponentbFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 34 | ref var @b = ref Unsafe.Add(ref intcomponentbFirstElement, entityIndex); 35 | IncrementAAndB(ref @a, ref @b); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/ParamQueryCompilation/ExpectedGeneration/ParamQuerySystem.IncrementANotC(ref IntComponentA).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class ParamQuerySystem 14 | { 15 | private static QueryDescription IncrementANotC_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA)), any: Signature.Null, none: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentC)), exclusive: Signature.Null); 16 | private static World? _IncrementANotC_Initialized; 17 | private static Query? _IncrementANotC_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void IncrementANotCQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementANotC_Initialized, world)) 22 | { 23 | _IncrementANotC_Query = world.Query(in IncrementANotC_QueryDescription); 24 | _IncrementANotC_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementANotC_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | foreach (var entityIndex in chunk) 31 | { 32 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 33 | IncrementANotC(ref @a); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/ParamQueryCompilation/ExpectedGeneration/ParamQuerySystem.IncrementOnlyAWithB(ref IntComponentA, in IntComponentB).g.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Arch.Core; 6 | using Arch.Core.Extensions; 7 | using Arch.Core.Utils; 8 | using ArrayExtensions = CommunityToolkit.HighPerformance.ArrayExtensions; 9 | using Component = Arch.Core.Component; 10 | 11 | namespace Arch.System.SourceGenerator.Tests 12 | { 13 | partial class ParamQuerySystem 14 | { 15 | private static QueryDescription IncrementOnlyAWithB_QueryDescription = new QueryDescription(all: new Signature(typeof(global::Arch.System.SourceGenerator.Tests.IntComponentA), typeof(global::Arch.System.SourceGenerator.Tests.IntComponentB)), any: Signature.Null, none: Signature.Null, exclusive: Signature.Null); 16 | private static World? _IncrementOnlyAWithB_Initialized; 17 | private static Query? _IncrementOnlyAWithB_Query; 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void IncrementOnlyAWithBQuery(World world) 20 | { 21 | if (!ReferenceEquals(_IncrementOnlyAWithB_Initialized, world)) 22 | { 23 | _IncrementOnlyAWithB_Query = world.Query(in IncrementOnlyAWithB_QueryDescription); 24 | _IncrementOnlyAWithB_Initialized = world; 25 | } 26 | 27 | foreach (ref var chunk in _IncrementOnlyAWithB_Query) 28 | { 29 | ref var @intcomponentaFirstElement = ref chunk.GetFirst(); 30 | ref var @intcomponentbFirstElement = ref chunk.GetFirst(); 31 | foreach (var entityIndex in chunk) 32 | { 33 | ref var @a = ref Unsafe.Add(ref intcomponentaFirstElement, entityIndex); 34 | ref var @_ = ref Unsafe.Add(ref intcomponentbFirstElement, entityIndex); 35 | IncrementOnlyAWithB(ref @a, in @_); 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/ParamQueryCompilation/ParamQuerySystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Arch.Core; 4 | using NUnit.Framework; 5 | 6 | namespace Arch.System.SourceGenerator.Tests; 7 | 8 | /// 9 | /// Tests queries using parameters. 10 | /// 11 | internal partial class ParamQuerySystem : BaseTestSystem 12 | { 13 | public ParamQuerySystem(World world) : base(world) { } 14 | 15 | [Query] 16 | public static void IncrementA(ref IntComponentA a) 17 | { 18 | a.Value++; 19 | } 20 | 21 | [Query] 22 | public static void IncrementOnlyAWithB(ref IntComponentA a, in IntComponentB _) 23 | { 24 | a.Value++; 25 | } 26 | 27 | [Query] 28 | [None(typeof(IntComponentC))] 29 | public static void IncrementANotC(ref IntComponentA a) 30 | { 31 | a.Value++; 32 | } 33 | 34 | [Query] 35 | public static void IncrementAAndB(ref IntComponentA a, ref IntComponentB b) 36 | { 37 | a.Value++; 38 | b.Value++; 39 | } 40 | 41 | private (Entity, Dictionary ComponentValues)[] _expectedComponentValues 42 | = Array.Empty<(Entity, Dictionary ComponentValues)>(); 43 | 44 | public override void Setup() 45 | { 46 | _expectedComponentValues = new[] 47 | { 48 | (World.Create(new IntComponentA()), 49 | new Dictionary { { typeof(IntComponentA), 0 } }), 50 | (World.Create(new IntComponentB()), 51 | new Dictionary { { typeof(IntComponentB), 0 } }), 52 | (World.Create(new IntComponentA(), new IntComponentB()), 53 | new Dictionary { { typeof(IntComponentA), 0 }, { typeof(IntComponentB), 0 } }), 54 | (World.Create(new IntComponentA(), new IntComponentB(), new IntComponentC()), 55 | new Dictionary { { typeof(IntComponentA), 0 }, { typeof(IntComponentB), 0 }, { typeof(IntComponentC), 0 } }) 56 | }; 57 | } 58 | 59 | private void TestExpectedValues() 60 | { 61 | foreach (var (e, values) in _expectedComponentValues) 62 | { 63 | foreach (var (type, expectedValue) in values) 64 | { 65 | var component = World.Get(e, type) as IIntComponent; 66 | Assert.That(component, Is.Not.Null); 67 | Assert.That(component.Value, Is.EqualTo(expectedValue)); 68 | } 69 | } 70 | } 71 | 72 | public override void Update(in int t) 73 | { 74 | TestExpectedValues(); 75 | 76 | IncrementAQuery(World); 77 | _expectedComponentValues[0].ComponentValues[typeof(IntComponentA)]++; 78 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 79 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentA)]++; 80 | TestExpectedValues(); 81 | 82 | IncrementOnlyAWithBQuery(World); 83 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 84 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentA)]++; 85 | TestExpectedValues(); 86 | 87 | IncrementANotCQuery(World); 88 | _expectedComponentValues[0].ComponentValues[typeof(IntComponentA)]++; 89 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 90 | TestExpectedValues(); 91 | 92 | IncrementAAndBQuery(World); 93 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentA)]++; 94 | _expectedComponentValues[2].ComponentValues[typeof(IntComponentB)]++; 95 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentA)]++; 96 | _expectedComponentValues[3].ComponentValues[typeof(IntComponentB)]++; 97 | TestExpectedValues(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/Shared/BaseTestSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.Core; 2 | 3 | namespace Arch.System.SourceGenerator.Tests; 4 | 5 | /// 6 | /// Provides a base class for test systems. This must be included in the compilation to ensure that the system is generated correctly. 7 | /// 8 | /// The world instance to which the system will be attached. 9 | internal abstract class BaseTestSystem : BaseSystem 10 | { 11 | protected BaseTestSystem(World world) : base(world) { } 12 | 13 | /// 14 | /// Sets up the system for testing. Create entities, components, and any other necessary state. 15 | /// 16 | public abstract void Setup(); 17 | 18 | /// 19 | /// Runs the test logic for the system. By default, it simply calls the update pipeline. 20 | /// 21 | public virtual void Test() 22 | { 23 | BeforeUpdate(0); 24 | Update(0); 25 | AfterUpdate(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/Shared/IntComponents.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.System.SourceGenerator.Tests; 2 | 3 | #pragma warning disable CS0649 // Allow fields to be unassigned for testing purposes 4 | internal interface IIntComponent 5 | { 6 | int Value { get; } 7 | } 8 | 9 | internal struct IntComponentA : IIntComponent 10 | { 11 | public int Value; 12 | 13 | readonly int IIntComponent.Value 14 | { 15 | get => Value; 16 | } 17 | } 18 | 19 | internal struct IntComponentB : IIntComponent 20 | { 21 | public int Value; 22 | 23 | readonly int IIntComponent.Value 24 | { 25 | get => Value; 26 | } 27 | } 28 | 29 | internal struct IntComponentC : IIntComponent 30 | { 31 | public int Value; 32 | 33 | readonly int IIntComponent.Value 34 | { 35 | get => Value; 36 | } 37 | } 38 | 39 | internal struct IntComponentD : IIntComponent 40 | { 41 | public int Value; 42 | 43 | readonly int IIntComponent.Value 44 | { 45 | get => Value; 46 | } 47 | } 48 | 49 | #pragma warning restore CS0649 50 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator.Tests/SystemsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Arch.Core; 3 | using NUnit.Framework; 4 | 5 | namespace Arch.System.SourceGenerator.Tests; 6 | 7 | /// 8 | /// Runs tests for systems in each compilation. 9 | /// Note that the compilation is shared across all tests, so the systems are not isolated. 10 | /// As a result, the tests may not be completely independent. However, they are easier to debug. 11 | /// Separately, the same tests are run in isolation in the Arch.System.SourceGenerator.Tests project. 12 | /// 13 | [TestFixture] 14 | internal sealed class SystemsTest 15 | { 16 | /// 17 | /// Tests a system by creating it and running its update method. 18 | /// 19 | /// 20 | /// The type of the system to test, which must inherit from BaseSystem and must have a constructor that takes a World parameter. 21 | /// 22 | private static void TestSystem() where T : BaseTestSystem 23 | { 24 | using var world = World.Create(); 25 | var system = Activator.CreateInstance(typeof(T), world) as T; 26 | Assert.That(system, Is.Not.Null, 27 | $"System instance {typeof(T).Name} should not be null. Ensure it has a constructor that takes a single World param."); 28 | system.Setup(); 29 | system.Test(); 30 | } 31 | 32 | [Test] 33 | public void BasicCompilation() 34 | { 35 | TestSystem(); 36 | } 37 | 38 | [Test] 39 | public void AttributeQueryCompilation() 40 | { 41 | TestSystem(); 42 | } 43 | 44 | [Test] 45 | public void ParamQueryCompilation() 46 | { 47 | TestSystem(); 48 | } 49 | 50 | [Test] 51 | public void DataParamCompilation() 52 | { 53 | TestSystem(); 54 | } 55 | 56 | [Test] 57 | public void GeneratedUpdateCompilation() 58 | { 59 | TestSystem(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator/Arch.System.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | enable 6 | enable 7 | latest 8 | 9 | true 10 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 11 | true 12 | snupkg 13 | 14 | Arch.System.SourceGenerator 15 | Arch.System.SourceGenerator 16 | 2.1.0 17 | genaray 18 | Apache-2.0 19 | A source generator for arch.system. 20 | Updated to fit Arch 2.1.0-beta and upwards. 21 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 22 | 23 | https://github.com/genaray/Arch.Extended 24 | https://github.com/genaray/Arch.Extended.git 25 | git 26 | true 27 | 28 | 12 29 | true 30 | Apache2.0 31 | 32 | en-US 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Arch.System.SourceGenerator/Extensions/IMethodSymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Arch.System.SourceGenerator; 4 | 5 | public static class IMethodSymbolExtensions 6 | { 7 | 8 | /// 9 | /// Searches attributes of a and returns the first one found. 10 | /// 11 | /// The instance. 12 | /// The attributes name. 13 | /// The attribute wrapped in an . 14 | public static AttributeData GetAttributeData(this IMethodSymbol ms, string name) 15 | { 16 | foreach (var attribute in ms.GetAttributes()) 17 | { 18 | var classSymbol = attribute.AttributeClass; 19 | if(!classSymbol.Name.Contains(name)) continue; 20 | 21 | return attribute; 22 | } 23 | 24 | return default; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator/Extensions/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Text; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Arch.System.SourceGenerator; 6 | 7 | public static class CommonUtils 8 | { 9 | 10 | /// 11 | /// Convert a to its code string equivalent. 12 | /// 13 | /// The . 14 | /// The code string equivalent. 15 | public static string RefKindToString(RefKind refKind) 16 | { 17 | switch (refKind) 18 | { 19 | case RefKind.None: 20 | return ""; 21 | case RefKind.Ref: 22 | return "ref"; 23 | case RefKind.In: 24 | return "in"; 25 | case RefKind.Out: 26 | return "out"; 27 | } 28 | return null; 29 | } 30 | 31 | /// 32 | /// Creates a list of generic type parameters separated by a simple comma. 33 | /// T0,T1,..TN 34 | /// 35 | /// The instance. 36 | /// The amount of generic type parameters. 37 | /// 38 | public static StringBuilder GenericsWithoutBrackets(this StringBuilder sb, int amount) 39 | { 40 | for (var i = 0; i < amount; i++) 41 | sb.Append($"T{i},"); 42 | if (sb.Length > 0) sb.Length -= 1; 43 | 44 | return sb; 45 | } 46 | 47 | /// 48 | /// Creates a list of generic type parameters types separated by a simple comma. 49 | /// typeof(T0),typeof(T1),..typeof(TN) 50 | /// 51 | /// The instance. 52 | /// The amount of generic type parameters. 53 | /// 54 | public static StringBuilder GenericsToTypeArray(this StringBuilder sb, int amount) 55 | { 56 | for (var i = 0; i < amount; i++) 57 | sb.Append($"typeof(T{i}),"); 58 | if (sb.Length > 0) sb.Length -= 1; 59 | 60 | return sb; 61 | } 62 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator/Model.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Arch.System.SourceGenerator; 4 | 5 | /// 6 | /// Represents the BaseSystem that is generated and calls its generated query methods. 7 | /// 8 | public struct BaseSystem 9 | { 10 | /// 11 | /// The namespace its generic is in. 12 | /// 13 | public string GenericTypeNamespace { get; set; } 14 | 15 | /// 16 | /// The namespace this system is in. 17 | /// 18 | public string Namespace { get; set; } 19 | 20 | /// 21 | /// Its name. 22 | /// 23 | public string Name { get; set; } 24 | 25 | /// 26 | /// The generic type. 27 | /// 28 | public ITypeSymbol GenericType { get; set; } 29 | 30 | /// 31 | /// The Query methods this base system calls one after another. 32 | /// 33 | public IList QueryMethods { get; set; } 34 | } 35 | 36 | /// 37 | /// Represents the Query method that is generated. 38 | /// 39 | public struct QueryMethod 40 | { 41 | /// 42 | /// If the class containing this Query method is within the global namespace. 43 | /// 44 | public bool IsGlobalNamespace { get; set; } 45 | 46 | /// 47 | /// The namespace of the method. 48 | /// 49 | public string Namespace { get; set; } 50 | 51 | /// 52 | /// If this method is static. 53 | /// 54 | public bool IsStatic { get; set; } 55 | 56 | /// 57 | /// If this Query method contains an Entity as a param and acesses it. 58 | /// 59 | public bool IsEntityQuery { get; set; } 60 | 61 | /// 62 | /// The name of the class containing this Query method. 63 | /// 64 | public string ClassName { get; set; } 65 | 66 | /// 67 | /// The name of the Query method. 68 | /// 69 | public string MethodName { get; set; } 70 | 71 | /// 72 | /// The entity parameter, if its an entity query. 73 | /// 74 | public IParameterSymbol EntityParameter { get; set; } 75 | 76 | /// 77 | /// All parameters within the query method, not only the components. Also Entity and Data annotated ones. 78 | /// public void Query([Data] float time, in Entity entity, ...); 79 | /// 80 | public IList Parameters { get; set; } 81 | 82 | /// 83 | /// The Components acessed within the query method. 84 | /// public void Query(ref Position pos, in Velocity vel){} 85 | /// 86 | public IList Components { get; set; } 87 | 88 | /// 89 | /// All s mentioned in the All annotation query filter. 90 | /// [All(typeof(Position), typeof(Velocity)] or its generic variant 91 | /// 92 | public IList AllFilteredTypes { get; set; } 93 | 94 | /// 95 | /// All s mentioned in the Any annotation query filter. 96 | /// [Any(typeof(Position), typeof(Velocity)] or its generic variant 97 | /// 98 | public IList AnyFilteredTypes { get; set; } 99 | 100 | /// 101 | /// All s mentioned in the None annotation query filter. 102 | /// [None(typeof(Position), typeof(Velocity)] or its generic variant 103 | /// 104 | public IList NoneFilteredTypes { get; set; } 105 | 106 | /// 107 | /// All s mentioned in the Exclusive annotation query filter. 108 | /// [Exclusive(typeof(Position), typeof(Velocity)] or its generic variant 109 | /// 110 | public IList ExclusiveFilteredTypes { get; set; } 111 | } -------------------------------------------------------------------------------- /Arch.System.SourceGenerator/SourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | using Microsoft.CodeAnalysis.Text; 8 | 9 | namespace Arch.System.SourceGenerator; 10 | 11 | [Generator] 12 | public class QueryGenerator : IIncrementalGenerator 13 | { 14 | private static Dictionary> _classToMethods { get; set; } 15 | 16 | public void Initialize(IncrementalGeneratorInitializationContext context) 17 | { 18 | //if (!Debugger.IsAttached) Debugger.Launch(); 19 | 20 | // Do a simple filter for methods marked with update 21 | IncrementalValuesProvider methodDeclarations = context.SyntaxProvider.CreateSyntaxProvider( 22 | static (s, _) => s is MethodDeclarationSyntax { AttributeLists.Count: > 0 }, 23 | static (ctx, _) => GetMethodSymbolIfAttributeof(ctx, "Arch.System.QueryAttribute") 24 | ).Where(static m => m is not null)!; // filter out attributed methods that we don't care about 25 | 26 | // Combine the selected enums with the `Compilation` 27 | IncrementalValueProvider<(Compilation, ImmutableArray)> compilationAndMethods = context.CompilationProvider.Combine(methodDeclarations.WithComparer(Comparer.Instance).Collect()); 28 | context.RegisterSourceOutput(compilationAndMethods, static (spc, source) => Generate(source.Item1, source.Item2, spc)); 29 | } 30 | 31 | /// 32 | /// Adds a to its class. 33 | /// Stores them in . 34 | /// 35 | /// The which will be added/mapped to its class. 36 | private static void AddMethodToClass(IMethodSymbol methodSymbol) 37 | { 38 | if (!_classToMethods.TryGetValue(methodSymbol.ContainingSymbol, out var list)) 39 | { 40 | list = new List(); 41 | _classToMethods[methodSymbol.ContainingSymbol] = list; 42 | } 43 | list.Add(methodSymbol); 44 | } 45 | 46 | /// 47 | /// Returns a if its annocated with a attribute of . 48 | /// 49 | /// Its . 50 | /// The attributes name. 51 | /// 52 | private static MethodDeclarationSyntax? GetMethodSymbolIfAttributeof(GeneratorSyntaxContext context, string name) 53 | { 54 | // we know the node is a EnumDeclarationSyntax thanks to IsSyntaxTargetForGeneration 55 | var enumDeclarationSyntax = (MethodDeclarationSyntax)context.Node; 56 | 57 | // loop through all the attributes on the method 58 | foreach (var attributeListSyntax in enumDeclarationSyntax.AttributeLists) 59 | { 60 | foreach (var attributeSyntax in attributeListSyntax.Attributes) 61 | { 62 | if (ModelExtensions.GetSymbolInfo(context.SemanticModel, attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) continue; 63 | 64 | var attributeContainingTypeSymbol = attributeSymbol.ContainingType; 65 | var fullName = attributeContainingTypeSymbol.ToDisplayString(); 66 | 67 | // Is the attribute the [EnumExtensions] attribute? 68 | if (fullName != name) continue; 69 | return enumDeclarationSyntax; 70 | } 71 | } 72 | 73 | // we didn't find the attribute we were looking for 74 | return null; 75 | } 76 | 77 | /// 78 | /// Generates queries and partial classes for the found marked methods. 79 | /// 80 | /// The . 81 | /// The array, the methods which we will generate queries and classes for. 82 | /// The . 83 | private static void Generate(Compilation compilation, ImmutableArray methods, SourceProductionContext context) 84 | { 85 | if (methods.IsDefaultOrEmpty) return; 86 | 87 | // Generate Query methods and map them to their classes 88 | _classToMethods = new(512); 89 | foreach (var methodSyntax in methods) 90 | { 91 | IMethodSymbol? methodSymbol = null; 92 | try 93 | { 94 | var semanticModel = compilation.GetSemanticModel(methodSyntax.SyntaxTree); 95 | methodSymbol = ModelExtensions.GetDeclaredSymbol(semanticModel, methodSyntax) as IMethodSymbol; 96 | } 97 | catch 98 | { 99 | //not update,skip 100 | continue; 101 | } 102 | 103 | AddMethodToClass(methodSymbol); 104 | 105 | var sb = new StringBuilder(); 106 | var method = sb.AppendQueryMethod(methodSymbol); 107 | var fileName = methodSymbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat).Replace('<', '{').Replace('>', '}'); 108 | context.AddSource($"{fileName}.g.cs",CSharpSyntaxTree.ParseText(method.ToString()).GetRoot().NormalizeWhitespace().ToFullString()); 109 | } 110 | 111 | // Creating class that calls the created methods after another. 112 | foreach (var classToMethod in _classToMethods) 113 | { 114 | var template = new StringBuilder().AppendBaseSystem(classToMethod).ToString(); 115 | if (string.IsNullOrEmpty(template)) continue; 116 | 117 | var fileName = (classToMethod.Key as INamedTypeSymbol).ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat).Replace('<', '{').Replace('>', '}'); 118 | context.AddSource($"{fileName}.g.cs", 119 | CSharpSyntaxTree.ParseText(template).GetRoot().NormalizeWhitespace().ToFullString()); 120 | } 121 | } 122 | 123 | /// 124 | /// Compares s to remove duplicates. 125 | /// 126 | class Comparer : IEqualityComparer 127 | { 128 | public static readonly Comparer Instance = new(); 129 | 130 | public bool Equals(MethodDeclarationSyntax x, MethodDeclarationSyntax y) 131 | { 132 | return x.Equals(y); 133 | } 134 | 135 | public int GetHashCode(MethodDeclarationSyntax obj) 136 | { 137 | return obj.GetHashCode(); 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /Arch.System/Arch.System.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0; net6.0; netstandard2.1 5 | enable 6 | 7 | true 8 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 9 | true 10 | snupkg 11 | 12 | Arch.System 13 | Arch.System 14 | 1.1.0 15 | genaray 16 | Apache-2.0 17 | A systems framework for arch. 18 | Made group extendible. 19 | c#;.net;.net6;.net7;ecs;game;entity;gamedev; game-development; game-engine; entity-component-system; arch; 20 | 21 | https://github.com/genaray/Arch.Extended 22 | https://github.com/genaray/Arch.Extended.git 23 | git 24 | true 25 | 26 | 11 27 | true 28 | Apache2.0 29 | 30 | en-US 31 | 32 | true 33 | 34 | 35 | 36 | 37 | TextTemplatingFileGenerator 38 | GenericAttributes.cs 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | True 49 | True 50 | GenericAttributes.tt 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Arch.System/Attributes.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.System; 2 | 3 | /// 4 | /// Marks a method to generate a high performance query for it. 5 | /// 6 | [global::System.AttributeUsage(global::System.AttributeTargets.Method)] 7 | public class QueryAttribute : global::System.Attribute 8 | { 9 | /// 10 | /// If set to true, Query will be run in parallel. 11 | /// 12 | public bool Parallel { get; set; } 13 | } 14 | 15 | /// 16 | /// Marks a parameter as "data". This will be taken into account during source generation and will still be passed as a parameter in the query method. 17 | /// Is not treated as an entity component. 18 | /// 19 | [global::System.AttributeUsage(global::System.AttributeTargets.Parameter)] 20 | public class DataAttribute : global::System.Attribute 21 | { 22 | } 23 | 24 | /// 25 | /// Defines a set of components each entity requires. 26 | /// 27 | [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] 28 | public class AllAttribute : global::System.Attribute 29 | { 30 | /// 31 | /// The types of the component. 32 | /// 33 | public Type[] ComponentTypes { get; } 34 | 35 | /// 36 | /// Constructs an All attribute with the specified component types. 37 | /// 38 | /// The types of the components that should be present. 39 | public AllAttribute(params Type[] componentTypes) 40 | { 41 | ComponentTypes = componentTypes; 42 | } 43 | } 44 | 45 | /// 46 | /// Defines a set of components each entity requires any from. 47 | /// 48 | [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] 49 | public class AnyAttribute : global::System.Attribute 50 | { 51 | /// 52 | /// The types of the component. 53 | /// 54 | public Type[] ComponentTypes { get; } 55 | 56 | /// 57 | /// Constructs an Any attribute with the specified component types. 58 | /// 59 | /// The types of the components that can be present. 60 | public AnyAttribute(params Type[] componentTypes) 61 | { 62 | ComponentTypes = componentTypes; 63 | } 64 | } 65 | 66 | /// 67 | /// Defines a set of components none of the entities should have. 68 | /// 69 | [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] 70 | public class NoneAttribute : global::System.Attribute 71 | { 72 | 73 | /// 74 | /// The types of the component. 75 | /// 76 | public Type[] ComponentTypes { get; } 77 | 78 | /// 79 | /// Constructs a None attribute with the specified component types. 80 | /// 81 | /// The types of the components that should not be present. 82 | public NoneAttribute(params Type[] componentTypes) 83 | { 84 | ComponentTypes = componentTypes; 85 | } 86 | } 87 | 88 | /// 89 | /// Defines an exclusive set of components an entity should have. 90 | /// 91 | [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)] 92 | public class ExclusiveAttribute : global::System.Attribute 93 | { 94 | /// 95 | /// The types of the component. 96 | /// 97 | public Type[] ComponentTypes { get; } 98 | 99 | /// 100 | /// Constructs an exclusive attribute with the specified component types. 101 | /// 102 | /// The types of the components that should be present exclusively. 103 | public ExclusiveAttribute(params Type[] componentTypes) 104 | { 105 | ComponentTypes = componentTypes; 106 | } 107 | } -------------------------------------------------------------------------------- /Arch.System/Templates/GenericAttributes.tt: -------------------------------------------------------------------------------- 1 | namespace Arch.System; 2 | <#@ template language="C#" #> 3 | <#@ output extension=".cs" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ include file="Helpers.ttinclude" #> 6 | #if NET7_0_OR_GREATER 7 | 8 | <# 9 | var names = new string[] { "All", "Any", "None", "Exclusive" }; 10 | foreach (var name in names) 11 | { 12 | for (var index = 1; index <= Amount; index++) 13 | { 14 | var generics = AppendGenerics(index); 15 | var parameters = AppendGenericTypeParameters(index); 16 | #> 17 | /// 18 | public class <#= name #>Attribute<<#= generics #>> : <#= name #>Attribute 19 | { 20 | /// 21 | public <#= name #>Attribute() : base(<#= parameters #>) { } 22 | } 23 | 24 | <# 25 | } 26 | } 27 | #> 28 | #endif 29 | -------------------------------------------------------------------------------- /Arch.System/Templates/Helpers.ttinclude: -------------------------------------------------------------------------------- 1 | <#@ import namespace="System.Text" #> 2 | <#@ import namespace="System.Collections.Generic" #> 3 | <#+ 4 | public int Amount = 25; 5 | 6 | public string Indent(StringBuilder sb, int spaces) 7 | { 8 | var indent = new string(' ', spaces); 9 | return sb.ToString().Replace("\n", "\n" + indent); 10 | } 11 | 12 | string AppendGenerics(int amount) 13 | { 14 | var sb = new StringBuilder(); 15 | for (var i = 0; i < amount; i++) 16 | { 17 | if (i > 0) sb.Append(", "); 18 | sb.Append($"T{i}"); 19 | } 20 | return sb.ToString(); 21 | } 22 | 23 | /// 24 | /// Lists generic types in a row as parameters. 25 | /// 26 | /// 27 | /// typeof(T0), typeof(T1), ... 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | public StringBuilder AppendGenericTypeParameters(int amount) 35 | { 36 | var sb = new StringBuilder(); 37 | for (var localIndex = 0; localIndex < amount; localIndex++) 38 | { 39 | sb.Append($"typeof(T{localIndex}), "); 40 | } 41 | 42 | sb.Length -= 2; 43 | return sb; 44 | } 45 | #> -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Release 7 | false 8 | en 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arch.Extended 2 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=for-the-badge)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity) 3 | [![Nuget](https://img.shields.io/nuget/v/Arch?style=for-the-badge)](https://www.nuget.org/packages/Arch.System/) 4 | [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0) 5 | ![C#](https://img.shields.io/badge/c%23-%23239120.svg?style=for-the-badge&logo=c-sharp&logoColor=white) 6 | 7 | Extensions for [Arch](https://github.com/genaray/Arch) with some useful features like Systems, Source Generator and Utils. 8 | 9 | - 🛠️ **_Productive_** > Adds some useful tools and features to the main repository! 10 | - ☕️ **_SIMPLE_** > Works easily, reliably and understandably! 11 | - 💪 _**MAINTAINED**_ > It's actively being worked on, maintained, and supported! 12 | - 🚢 _**SUPPORT**_ > Supports .NetStandard 2.1, .Net Core 6 and 7 and therefore you may use it with Unity or Godot! 13 | 14 | Download the packages and get started today! 15 | ```console 16 | dotnet add package Arch.System --version 1.1.0 17 | dotnet add package Arch.System.SourceGenerator --version 2.1.0 18 | dotnet add package Arch.EventBus --version 1.0.2 19 | dotnet add package Arch.LowLevel --version 1.1.5 20 | dotnet add package Arch.Relationships --version 1.0.0 21 | dotnet add package Arch.Persistence --version 2.0.0 22 | dotnet add package Arch.AOT.SourceGenerator --version 1.0.1 23 | ``` 24 | 25 | # Features & Tools 26 | - ⚙️ **_[Systems](https://github.com/genaray/Arch.Extended/wiki/Systems-API)_** > By means of systems, it is now easy to organize, reuse and arrange queries. 27 | - ✍️ **_[Source Generator](https://github.com/genaray/Arch.Extended/wiki/Source-Generator)_** > Declarative syntax using attributes and source generator, let your queries write themselves! 28 | - ✉️ **_[EventBus](https://github.com/genaray/Arch.Extended/wiki/EventBus)_** > A source generated EventBus, send Events with high-performance! 29 | - 👾 **_[LowLevel](https://github.com/genaray/Arch.Extended/wiki/Lowlevel-&-Resource-Management)_** > Low-level utils and data structures to get rid of GC pressure! 30 | - 💑 **_[Relationships](https://github.com/genaray/Arch.Extended/wiki/Relationships)_** > Adds simple relationships between entities to arch! 31 | - 💾 **_[Persistence](https://github.com/genaray/Arch.Extended/wiki/Persistence)_** > JSON and Binary (de)serialization to persist your Worlds! 32 | - ⌛ **_[AOT Source Generator](https://github.com/genaray/Arch.Extended/wiki/AOT-Source-Generator)_** > Helps with AOT compatibility and reduces boilerplate code! 33 | > Check the links and the [Wiki](https://github.com/genaray/Arch.Extended/wiki)! 34 | 35 | # Full code sample 36 | 37 | With this package you are able to write and group queries and systems for Arch automatically. 38 | And all this with the best possible performance. 39 | 40 | The tools can be used independently of each other. 41 | 42 | ```cs 43 | // Components ( ignore the formatting, this saves space ) 44 | public struct Position{ float X, Y }; 45 | public struct Velocity{ float Dx, Dy }; 46 | 47 | // BaseSystem provides several useful methods for interacting and structuring systems 48 | public class MovementSystem : BaseSystem 49 | { 50 | public MovementSystem(World world) : base(world) {} 51 | 52 | // Generates a query and calls that one automatically on BaseSystem.Update 53 | [Query] 54 | public void Move([Data] in float time, ref Position pos, ref Velocity vel) 55 | { 56 | pos.X += time * vel.X; 57 | pos.Y += time * vel.Y; 58 | } 59 | 60 | // Generates and filters a query and calls that one automatically on BaseSystem.Update in order 61 | [Query] 62 | [All, Any, None] // Attributes also accept non generics :) 63 | public void ResetVelocity(ref Velocity vel) 64 | { 65 | vel = new Velocity{ X = 0, Y = 0 }; 66 | } 67 | } 68 | 69 | public class Game 70 | { 71 | public static void Main(string[] args) 72 | { 73 | var deltaTime = 0.05f; // This is mostly given by engines, frameworks 74 | 75 | // Create a world and a group of systems which will be controlled 76 | var world = World.Create(); 77 | var _systems = new Group( 78 | "Systems", 79 | new MovementSystem(world), // Run in order 80 | new MyOtherSystem(...), 81 | ... 82 | ); 83 | 84 | _systems.Initialize(); // Inits all registered systems 85 | _systems.BeforeUpdate(in deltaTime); // Calls .BeforeUpdate on all systems ( can be overriden ) 86 | _systems.Update(in deltaTime); // Calls .Update on all systems ( can be overriden ) 87 | _systems.AfterUpdate(in deltaTime); // Calls .AfterUpdate on all System ( can be overriden ) 88 | _systems.Dispose(); // Calls .Dispose on all systems ( can be overriden ) 89 | } 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /scripts/UnityPublish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Publishes Unity release to dist/Assemblies using only netstandard2.0 and netstandard2.1 4 | ######################################################################################### 5 | 6 | dotnet restore 7 | 8 | assemblyDir="`pwd`/dist/Assemblies" 9 | 10 | rm -rf "${assemblyDir}" 11 | 12 | mkdir -p "${assemblyDir}" 13 | 14 | dotnet msbuild /t:Unity \ 15 | -p:PublishDir="${assemblyDir}" \ 16 | -p:TargetFramework=netstandard2.1 \ 17 | -p:TargetFrameworks=netstandard2.1 \ 18 | -p:TargetFrameworkVersion=v2.1 19 | --------------------------------------------------------------------------------