├── .github └── workflows │ └── workflows.yaml ├── .gitignore ├── Arch.SystemGroups.SourceGenerator ├── AddAllSystemsGenerator.cs ├── Arch.SystemGroups.SourceGenerator.csproj ├── AttributesUtils.cs ├── CommonUtils.cs ├── CreateGroupGenerator.cs ├── EdgesGenerator.cs ├── GroupInfo.cs ├── InjectToWorldGenerator.cs ├── MetadataGenerator.cs ├── PartialClassGenerator.cs ├── Properties │ └── launchSettings.json ├── SourceGenerator.cs ├── SystemInfo.cs └── WorldInfo.cs ├── Arch.SystemGroups.Tests ├── Arch.SystemGroups.Tests.csproj ├── AssertHelpers.cs ├── CentralizedThrottling │ ├── CentralizedThrottlingTests.cs │ ├── Groups │ │ ├── ImplicitThrottlingNestedGroup.cs │ │ ├── NoThrottlingRootGroup.cs │ │ ├── ThrottlingNestedGroup.cs │ │ └── ThrottlingRootGroup.cs │ └── Systems │ │ ├── CentralizedThrottlingTestSystemBase.cs │ │ ├── ImplicitThrottlingExplicitNestedSystem.cs │ │ ├── ImplicitThrottlingImplicitNestedSystem.cs │ │ ├── NoThrottlingNestedSystem.cs │ │ ├── NoThrottlingRootSystem.cs │ │ └── ThrottlingRootSystem.cs ├── DescriptorTests │ ├── DescriptorTests.cs │ ├── NestedDescriptorTests.cs │ └── SystemGroups │ │ ├── InitSystemGroupSystemTest.cs │ │ ├── PhysicsSystemGroupSystemTest.cs │ │ ├── PostPhysicsSystemGroupSystemTest.cs │ │ ├── PostRenderingSystemGroupSystemTest.cs │ │ ├── PresentationSystemGroupSystemTest.cs │ │ └── SimulationSystemGroupSystemTest.cs ├── DisconnectedDependencies │ ├── DisconnectedDependenciesTests.cs │ ├── Groups.cs │ └── Systems.cs ├── ExceptionsHandling │ ├── ExceptionsHandlingTests.cs │ └── Systems.cs ├── GenericSetup │ ├── GenericSetupTests.cs │ ├── GenericSystem0.cs │ ├── GenericSystem1.cs │ ├── GenericSystem2.cs │ ├── GenericSystem3.cs │ ├── Group1OfGenerics.cs │ ├── Group2OfGenerics.cs │ └── NestedArgument.cs ├── MetadataTests │ ├── Custom1Attribute.cs │ ├── Custom2Attribute.cs │ ├── Custom3Attribute.cs │ ├── Custom4Attribute.cs │ ├── Groups │ │ ├── MetadataGroup1.cs │ │ └── MetadataNestedGroup1.cs │ ├── MetadataTests.cs │ └── Systems │ │ ├── MetadataSystem1.cs │ │ └── MetadataSystem2.cs ├── NestedGroups │ ├── NestedGroups.cs │ ├── NestedGroupsTests.cs │ └── RootGroup.cs ├── PlayerLoopTests │ ├── OrderedSystemGroupAggregateTests.cs │ └── PlayerLoopHelperTests.cs ├── RedundantDependencies │ ├── RedundantDependenciesSystem.cs │ └── RedundantDependenciesTests.cs ├── TestSetup1 │ ├── CustomClass1.cs │ ├── Groups │ │ └── CustomGroup1.cs │ ├── Systems │ │ ├── CustomSystem1.cs │ │ ├── CustomSystem1InCustomGroup1.cs │ │ ├── CustomSystemWithParameters1.cs │ │ ├── CustomSystemWithParameters2.cs │ │ └── DisconnectedSystem.cs │ ├── TestSetup1Tests.cs │ └── TestWorld.cs ├── TestSetup2 │ ├── Groups │ │ ├── GroupsA.cs │ │ └── GroupsB.cs │ ├── Systems │ │ ├── SystemsA.cs │ │ └── SystemsB.cs │ ├── TestSetup2Tests.cs │ └── TestWorld2.cs ├── ThrottleGroup │ ├── ParametrisedThrottleGroup.cs │ ├── ParametrisedThrottleSystem.cs │ ├── ThrottleGroupBase.cs │ ├── ThrottleGroupTests.cs │ ├── ThrottlePostRenderingGroup.cs │ ├── ThrottlePostRenderingSystem.cs │ ├── ThrottleSimulationGroup.cs │ └── ThrottleSimulationSystem.cs └── Usings.cs ├── Arch.SystemGroups.sln ├── Arch.SystemGroups ├── Arch.SystemGroups.NoUnity.csproj ├── Arch.SystemGroups.NoUnity.csproj.meta ├── Arch.SystemGroups.asmdef ├── Arch.SystemGroups.asmdef.meta ├── Arch.SystemGroups.csproj ├── Arch.SystemGroups.csproj.DotSettings ├── Arch.SystemGroups.csproj.DotSettings.meta ├── Arch.SystemGroups.csproj.meta ├── AssemblyInfo.cs ├── AssemblyInfo.cs.meta ├── Builder.meta ├── Builder │ ├── ArchSystemsWorldBuilder.cs │ ├── ArchSystemsWorldBuilder.cs.meta │ ├── ArchSystemsWorldBuilderExtensions.cs │ └── ArchSystemsWorldBuilderExtensions.cs.meta ├── DefaultSystemGroups.meta ├── DefaultSystemGroups │ ├── InitializationSystemGroup.cs │ ├── InitializationSystemGroup.cs.meta │ ├── PhysicsSystemGroup.cs │ ├── PhysicsSystemGroup.cs.meta │ ├── PostPhysicsSystemGroup.cs │ ├── PostPhysicsSystemGroup.cs.meta │ ├── PostRenderingSystemGroup.cs │ ├── PostRenderingSystemGroup.cs.meta │ ├── PreRenderingSystemGroup.cs │ ├── PreRenderingSystemGroup.cs.meta │ ├── PresentationSystemGroup.cs │ ├── PresentationSystemGroup.cs.meta │ ├── SimulationSystemGroup.cs │ ├── SimulationSystemGroup.cs.meta │ ├── SystemGroupsUtils.cs │ └── SystemGroupsUtils.cs.meta ├── Descriptors.meta ├── Descriptors │ ├── Descriptor.cs │ ├── Descriptor.cs.meta │ ├── SystemGroupWorldExtensions.cs │ └── SystemGroupWorldExtensions.cs.meta ├── Directory.Build.props ├── Directory.Build.props.meta ├── ExecutionNode.cs ├── ExecutionNode.cs.meta ├── Groups.meta ├── Groups │ ├── CustomGroupBase.cs │ ├── CustomGroupBase.cs.meta │ ├── DefaultGroup.cs │ ├── DefaultGroup.cs.meta │ ├── GroupNotFoundException.cs │ └── GroupNotFoundException.cs.meta ├── ISystemGroupExceptionHandler.cs ├── ISystemGroupExceptionHandler.cs.meta ├── Metadata.meta ├── Metadata │ ├── AttributesInfo.cs │ ├── AttributesInfo.cs.meta │ ├── SystemGroupAttributesInfo.cs │ └── SystemGroupAttributesInfo.cs.meta ├── PlayerLoopHelper.meta ├── PlayerLoopHelper │ ├── Aggregation.meta │ ├── Aggregation │ │ ├── ISystemGroupAggregate.cs │ │ ├── ISystemGroupAggregate.cs.meta │ │ ├── ISystemGroupAggregateFactory.cs │ │ ├── ISystemGroupAggregateFactory.cs.meta │ │ ├── OrderedSystemGroupAggregate.cs │ │ ├── OrderedSystemGroupAggregate.cs.meta │ │ ├── SystemGroupAggregate.cs │ │ ├── SystemGroupAggregate.cs.meta │ │ ├── SystemGroupAggregateCache.cs │ │ └── SystemGroupAggregateCache.cs.meta │ ├── DebouncedComparer.cs │ ├── DebouncedComparer.cs.meta │ ├── IPlayerLoop.cs │ ├── IPlayerLoop.cs.meta │ ├── PlayerLoopAddMode.cs │ ├── PlayerLoopAddMode.cs.meta │ ├── PlayerLoopHelper.cs │ ├── PlayerLoopHelper.cs.meta │ ├── UnityPlayerLoop.cs │ └── UnityPlayerLoop.cs.meta ├── PlayerLoopSystem.cs ├── PlayerLoopSystem.cs.meta ├── SystemGroup.cs ├── SystemGroup.cs.meta ├── SystemGroupWorld.cs ├── SystemGroupWorld.cs.meta ├── SystemsDependencies.meta ├── SystemsDependencies │ ├── ArchSystemsSorter.cs │ ├── ArchSystemsSorter.cs.meta │ ├── DisconnectedDependenciesFoundException.cs │ ├── DisconnectedDependenciesFoundException.cs.meta │ ├── DisconnectedDependenciesInfo.cs │ └── DisconnectedDependenciesInfo.cs.meta ├── SystemsUpdateOrder.cs ├── SystemsUpdateOrder.cs.meta ├── Throttling.meta ├── Throttling │ ├── ISystemGroupThrottler.cs │ ├── ISystemGroupThrottler.cs.meta │ ├── ThrottlingEnabledAttribute.cs │ └── ThrottlingEnabledAttribute.cs.meta ├── UnityBridge.meta ├── UnityBridge │ ├── TimeProvider.cs │ └── TimeProvider.cs.meta ├── UpdateInGroupAttribute.cs ├── UpdateInGroupAttribute.cs.meta ├── package.json └── package.json.meta ├── LICENSE ├── README.md └── libs └── UnityEngine.CoreModule.dll /.github/workflows/workflows.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: "Continuous Integration" 5 | 6 | on: 7 | push: 8 | paths-ignore: 9 | - '**/*.md' 10 | - '**/*.gitignore' 11 | - '**/*.gitattributes' 12 | pull_request: 13 | paths-ignore: 14 | - '**/*.md' 15 | - '**/*.gitignore' 16 | - '**/*.gitattributes' 17 | workflow_dispatch: 18 | branches: 19 | - main 20 | paths-ignore: 21 | - '**/*.md' 22 | - '**/*.gitignore' 23 | - '**/*.gitattributes' 24 | 25 | jobs: 26 | build: 27 | name: Build Arch.SystemGroups 28 | runs-on: ubuntu-latest 29 | env: 30 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 31 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 32 | DOTNET_NOLOGO: true 33 | DOTNET_GENERATE_ASPNET_CERTIFICATE: false 34 | DOTNET_ADD_GLOBAL_TOOLS_TO_PATH: false 35 | DOTNET_MULTILEVEL_LOOKUP: 0 36 | DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: true 37 | TERM: xterm 38 | 39 | strategy: 40 | fail-fast: false 41 | 42 | steps: 43 | - uses: actions/checkout@v3 44 | 45 | - name: Setup .NET SDK 46 | uses: actions/setup-dotnet@v3 47 | with: 48 | dotnet-version: | 49 | 6.0.x 50 | 7.0.x 51 | 52 | - name: Restore 53 | run: dotnet restore 54 | 55 | - name: Build 56 | run: dotnet build --configuration Release --no-restore 57 | 58 | - name: Test 59 | run: dotnet test --logger trx --results-directory "TestResults" 60 | 61 | - name: Upload dotnet test results 62 | uses: actions/upload-artifact@v3 63 | with: 64 | name: dotnet-results 65 | path: TestResults 66 | # Use always() to always run this step to publish test results when there are test failures 67 | if: ${{ always() }} -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/AddAllSystemsGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Arch.SystemGroups.SourceGenerator; 6 | 7 | public static class AddAllSystemsGenerator 8 | { 9 | internal static string GenerateExtensions(WorldsInfo worldsInfo) 10 | { 11 | var fileBuilder = new StringBuilder(); 12 | 13 | foreach (var pair in worldsInfo.Worlds) 14 | { 15 | var worldType = pair.Key; 16 | var worldInfo = pair.Value; 17 | 18 | var methods = new StringBuilder(); 19 | 20 | foreach (var worldInfoGroup in worldInfo.Groups) 21 | { 22 | GetInjectToWorldMethodForUniqueArgsCombination(methods, worldType, worldInfoGroup.Value.ArgsString, 23 | worldInfoGroup.Value.PassString, worldInfoGroup.Value.Types); 24 | } 25 | 26 | var classDeclaration = 27 | $$""" 28 | public static class {{worldType.Name}}InjectionExtensions 29 | { 30 | {{methods}} 31 | } 32 | """; 33 | 34 | fileBuilder.AppendLine(classDeclaration); 35 | } 36 | 37 | var template = 38 | $$""" 39 | using System; 40 | namespace Arch.SystemGroups 41 | { 42 | {{fileBuilder}} 43 | } 44 | """; 45 | 46 | return template; 47 | } 48 | 49 | private static void GetInjectToWorldMethodForUniqueArgsCombination(StringBuilder sb, ITypeSymbol worldType, string args, string pass, 50 | IList types) 51 | { 52 | var injectCalls = GetEachInjectToWorld(pass, types); 53 | 54 | var template = $$""" 55 | public static ref ArchSystemsWorldBuilder<{{worldType}}> AddAllSystems(this ref ArchSystemsWorldBuilder<{{worldType}}> worldBuilder{{args}}) 56 | { 57 | {{injectCalls}} 58 | return ref worldBuilder; 59 | } 60 | """; 61 | 62 | static StringBuilder GetEachInjectToWorld(string pass, IList types) 63 | { 64 | var sb = new StringBuilder(); 65 | foreach (var type in types) 66 | { 67 | sb.AppendLine( 68 | $"{CommonUtils.GetTypeReferenceInGlobalNotation(type)}.InjectToWorld(ref worldBuilder{pass});"); 69 | } 70 | 71 | return sb; 72 | } 73 | 74 | sb.Append(template); 75 | } 76 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/Arch.SystemGroups.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | preview 6 | true 7 | Generated 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/AttributesUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Arch.SystemGroups.SourceGenerator; 6 | 7 | public static class AttributesUtils 8 | { 9 | public readonly struct ActionOnAttribute 10 | { 11 | public readonly Action Action; 12 | public readonly Predicate Predicate; 13 | public readonly bool Recursively; 14 | 15 | public ActionOnAttribute(Action action, Predicate predicate, bool recursively) 16 | { 17 | Action = action; 18 | Recursively = recursively; 19 | Predicate = predicate; 20 | } 21 | 22 | public static implicit operator ActionOnAttribute((Action action, Predicate predicate, bool recursively) tuple) 23 | { 24 | return new ActionOnAttribute(tuple.action, tuple.predicate, tuple.recursively); 25 | } 26 | } 27 | 28 | public static void DoOnAttributes(ITypeSymbol type, params ActionOnAttribute[] actions) 29 | { 30 | static void DoOnAttributesInternal(ITypeSymbol currentType, ActionOnAttribute[] actions, ref int resolvedMask) 31 | { 32 | var attributes = currentType.GetAttributes(); 33 | 34 | for (var index = 0; index < actions.Length; index++) 35 | { 36 | // already resolved 37 | if ((resolvedMask & (1 << index)) > 0) 38 | continue; 39 | 40 | var action = actions[index]; 41 | 42 | foreach (var attribute in attributes) 43 | { 44 | if (action.Predicate(attribute)) 45 | { 46 | action.Action(attribute); 47 | resolvedMask |= 1 << index; 48 | } 49 | } 50 | 51 | if (!action.Recursively) 52 | resolvedMask |= 1 << index; 53 | } 54 | 55 | // if there are unresolved attributes call onto the base type 56 | if (resolvedMask != (~0 >> (sizeof(int) * 8 - actions.Length)) && currentType.BaseType != null) 57 | { 58 | DoOnAttributesInternal(currentType.BaseType, actions, ref resolvedMask); 59 | } 60 | } 61 | 62 | var flags = 0; 63 | DoOnAttributesInternal(type, actions, ref flags); 64 | } 65 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/CreateGroupGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.SourceGenerator; 2 | 3 | public static class CreateGroupGenerator 4 | { 5 | public static string GetTryGetCreateGroup(in GroupInfo groupInfo) 6 | { 7 | return groupInfo.Behaviour is { IsCustom: true, HasParameterlessConstructor: false } 8 | ? $"worldBuilder.TryRegisterGroup<{groupInfo.ClassName}>(typeof({groupInfo.UpdateInGroup}), {EdgesGenerator.AddEdgesCachedFieldName}, {EdgesGenerator.ValidateEdgesCachedFieldName}, {groupInfo.ThrottlingEnabled.ToString().ToLower()});" 9 | : $"worldBuilder.TryCreateGroup<{groupInfo.ClassName}>(typeof({groupInfo.UpdateInGroup}), {EdgesGenerator.AddEdgesCachedFieldName}, {EdgesGenerator.ValidateEdgesCachedFieldName}, {groupInfo.ThrottlingEnabled.ToString().ToLower()});"; 10 | } 11 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/EdgesGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Arch.SystemGroups.SourceGenerator 7 | { 8 | public static class EdgesGenerator 9 | { 10 | public const string AddEdgesCachedFieldName = "_addEdgesCached"; 11 | public const string ValidateEdgesCachedFieldName = "_validateEdgesCached"; 12 | public const string ValidateEdgesMethodName = "ValidateEdges"; 13 | public const string DisconnectedDependenciesFieldName = "disconnectedDependencies"; 14 | 15 | public static string GetValidateEdgesCachedField() => 16 | $"private static readonly Action> {ValidateEdgesCachedFieldName} = {ValidateEdgesMethodName};"; 17 | 18 | public static string GetValidateEdgesBody(IList updateBefore, IList updateAfter, 19 | ITypeSymbol group, string className, ITypeSymbol thisType, string typeGenericArguments) 20 | { 21 | var builder = new StringBuilder(); 22 | 23 | foreach (var dependency in updateBefore) 24 | InsertValidateEdge(dependency); 25 | 26 | foreach (var dependency in updateAfter) 27 | InsertValidateEdge(dependency); 28 | 29 | return builder.ToString(); 30 | 31 | void InsertValidateEdge(ITypeSymbol dependency) 32 | { 33 | // Filter out references to self 34 | if (dependency.Equals(thisType, SymbolEqualityComparer.Default)) 35 | return; 36 | 37 | builder.AppendFormat( 38 | $"ArchSystemsSorter.ValidateEdge({DisconnectedDependenciesFieldName}, typeof({className}{typeGenericArguments}), typeof({group}), typeof({dependency}), {dependency}.Metadata.UpdateInGroup);"); 39 | } 40 | } 41 | 42 | public static string GetAddEdgesCachedField() => 43 | $"private static readonly Action>> {AddEdgesCachedFieldName} = AddEdges;"; 44 | 45 | public static StringBuilder GetAddEdgesBody(IList updateBefore, IList updateAfter, string className, ITypeSymbol thisType, string typeGenericArguments) 46 | { 47 | var builder = new StringBuilder(); 48 | 49 | foreach (var typeSymbol in updateAfter) 50 | { 51 | // Filter out references to self 52 | if (typeSymbol.Equals(thisType, SymbolEqualityComparer.Default)) 53 | continue; 54 | 55 | // Update After = from That Type to This 56 | builder.AppendLine( 57 | $"ArchSystemsSorter.AddEdge(typeof({typeSymbol}), typeof({className}{typeGenericArguments}), edgesMap);"); 58 | } 59 | 60 | foreach (var typeSymbol in updateBefore) 61 | { 62 | if (typeSymbol.Equals(thisType, SymbolEqualityComparer.Default)) 63 | continue; 64 | 65 | // Update Before = from This Type to That 66 | builder.AppendLine( 67 | $"ArchSystemsSorter.AddEdge(typeof({className}{typeGenericArguments}), typeof({typeSymbol}), edgesMap);"); 68 | } 69 | 70 | if (builder.Length == 0) 71 | builder.AppendLine("// No Dependencies"); 72 | 73 | return builder; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/GroupInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace Arch.SystemGroups.SourceGenerator; 5 | 6 | /// 7 | /// Groups do not implement interface 8 | /// and do not contain constructor 9 | /// 10 | public struct GroupInfo 11 | { 12 | public struct BehaviourInfo 13 | { 14 | /// 15 | /// Indicates if the group inherits from 16 | /// 17 | public bool IsCustom { get; set; } 18 | 19 | /// 20 | /// If the group does nto have a parameterless constructor then it must be manually injected into the builder 21 | /// 22 | public bool HasParameterlessConstructor { get; set; } 23 | } 24 | 25 | /// 26 | /// This type of the class containing the Update Attributes. 27 | /// 28 | public ITypeSymbol This { get; set; } 29 | 30 | /// 31 | /// If the class containing Update Attributes method is within the global namespace. 32 | /// 33 | public bool IsGlobalNamespace { get; set; } 34 | 35 | /// 36 | /// Public, internal or private 37 | /// 38 | public string AccessModifier { get; set; } 39 | 40 | /// 41 | /// The namespace of the method. 42 | /// 43 | public string Namespace { get; set; } 44 | 45 | /// 46 | /// The name of the class containing these Update Attributes. 47 | /// 48 | public string ClassName { get; set; } 49 | 50 | /// 51 | /// Additional information about the group behaviour 52 | /// 53 | public BehaviourInfo Behaviour { get; set; } 54 | 55 | /// 56 | /// Multiple Attributes that are used to define the order of the system. 57 | /// [UpdateBefore(typeof(CustomSystem))] 58 | /// 59 | public IList UpdateBefore { get; set; } 60 | 61 | /// 62 | /// Multiple Attributes that are used to define the order of the system. 63 | /// [UpdateAfter(typeof(CustomSystem))] 64 | /// 65 | public IList UpdateAfter { get; set; } 66 | 67 | /// 68 | /// The type of the SystemGroup or CustomType 69 | /// 70 | public ITypeSymbol UpdateInGroup { get; set; } 71 | 72 | /// 73 | /// Throttling enabled for this group 74 | /// 75 | public bool ThrottlingEnabled { get; set; } 76 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/InjectToWorldGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace Arch.SystemGroups.SourceGenerator 5 | { 6 | public static class InjectToWorldGenerator 7 | { 8 | public static StringBuilder GetMethodArgumentsWithoutWorld(in SystemInfo systemInfo) 9 | { 10 | var sb = new StringBuilder(); 11 | // Assume that the first parameter is always the world 12 | for (var i = 0; i < systemInfo.ConstructorParams.Length; i++) 13 | { 14 | sb.Append(", "); 15 | var param = systemInfo.ConstructorParams[i]; 16 | 17 | sb.Append($"{CommonUtils.RefKindToString(param.RefKind)} {CommonUtils.GetTypeReferenceInGlobalNotation(param.Type)} {param.Name}" + 18 | $"{(param.HasExplicitDefaultValue ? " = " + (param.ExplicitDefaultValue ?? "default") : string.Empty)}"); 19 | } 20 | 21 | return sb; 22 | } 23 | 24 | public static StringBuilder GetPassArguments(in SystemInfo systemInfo) 25 | { 26 | var sb = new StringBuilder(); 27 | // Assume that the first parameter is always the world 28 | for (var i = 0; i < systemInfo.ConstructorParams.Length; i++) 29 | { 30 | sb.Append(", "); 31 | var param = systemInfo.ConstructorParams[i]; 32 | sb.Append($"{CommonUtils.RefKindToString(param.RefKind)} {param.Name}"); 33 | } 34 | 35 | return sb; 36 | } 37 | 38 | /// 39 | /// If the group is not a SystemGroup then the message to call that group creation will be called 40 | /// 41 | /// 42 | public static string GetGroupInjectionInvocation(in ITypeSymbol updateInGroup) 43 | { 44 | if (updateInGroup.BaseType?.Name == "SystemGroup" && updateInGroup.BaseType?.ContainingNamespace?.ToString() == "Arch.SystemGroups") 45 | return string.Empty; 46 | 47 | return $"{updateInGroup}.TryCreateGroup(ref worldBuilder);"; 48 | } 49 | 50 | public static StringBuilder GetAddToGroup(in SystemInfo systemInfo, string typeGenericArguments) 51 | { 52 | var sb = new StringBuilder(); 53 | 54 | sb.Append("worldBuilder.AddToGroup("); 55 | sb.Append("system, "); 56 | sb.Append($"typeof({systemInfo.UpdateInGroup}), "); 57 | sb.Append($"typeof({systemInfo.ClassName}{typeGenericArguments}), "); 58 | sb.Append(EdgesGenerator.AddEdgesCachedFieldName); 59 | sb.Append(", "); 60 | sb.Append(EdgesGenerator.ValidateEdgesCachedFieldName); 61 | sb.Append(", "); 62 | sb.Append(systemInfo.ThrottlingEnabled.ToString().ToLower()); 63 | sb.Append(");"); 64 | 65 | return sb; 66 | } 67 | 68 | public static StringBuilder GetSystemInstantiation(in SystemInfo systemInfo, string typeGenericArguments) 69 | { 70 | var sb = new StringBuilder("var system = new "); 71 | sb.Append(systemInfo.ClassName); 72 | sb.Append(typeGenericArguments); 73 | sb.Append("("); 74 | sb.Append("worldBuilder.World"); 75 | for (var i = 0; i < systemInfo.ConstructorParams.Length; i++) 76 | { 77 | sb.Append(", "); 78 | var param = systemInfo.ConstructorParams[i]; 79 | sb.Append($"{CommonUtils.RefKindToString(param.RefKind)} {param.Name}"); 80 | } 81 | sb.Append(");"); 82 | return sb; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/MetadataGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | 7 | namespace Arch.SystemGroups.SourceGenerator; 8 | 9 | public static class MetadataGenerator 10 | { 11 | private static string GenerateAttributesInstances( 12 | IReadOnlyList> groupedAttributes) 13 | { 14 | var sb = new StringBuilder(); 15 | 16 | foreach (var groupedAttribute in groupedAttributes) 17 | { 18 | sb.AppendLine( 19 | $"public static readonly IReadOnlyList<{groupedAttribute.Key}> {groupedAttribute.Key.Name}s = new {groupedAttribute.Key}[]"); 20 | 21 | sb.AppendLine("{"); 22 | 23 | foreach (var attributeData in groupedAttribute) 24 | { 25 | sb.Append(" new("); 26 | sb.Append(string.Join(", ", attributeData.ConstructorArguments.Select(x => x.ToCSharpStringSemanticallyValid()))); 27 | sb.AppendLine("),"); 28 | } 29 | 30 | sb.AppendLine("};"); 31 | sb.AppendLine(); 32 | } 33 | 34 | return sb.ToString(); 35 | } 36 | 37 | private static string GenerateGetFunction(IReadOnlyList> groupedAttributes) 38 | { 39 | var sb = new StringBuilder(); 40 | 41 | sb.AppendLine("public override T GetAttribute()"); 42 | sb.AppendLine("{"); 43 | 44 | foreach (var groupedAttribute in groupedAttributes) 45 | { 46 | sb.AppendLine( 47 | $" if (typeof(T) == typeof({groupedAttribute.Key}))"); 48 | sb.AppendLine( 49 | $" return (T)(object){groupedAttribute.Key.Name}s[0];"); 50 | } 51 | 52 | sb.AppendLine("return null;"); 53 | sb.AppendLine("}"); 54 | 55 | return sb.ToString(); 56 | } 57 | 58 | private static string GenerateGetListFunction(IReadOnlyList> groupedAttributes) 59 | { 60 | var sb = new StringBuilder(); 61 | 62 | sb.AppendLine("public override IReadOnlyList GetAttributes()"); 63 | sb.AppendLine("{"); 64 | 65 | foreach (var groupedAttribute in groupedAttributes) 66 | { 67 | sb.AppendLine( 68 | $" if (typeof(T) == typeof({groupedAttribute.Key}))"); 69 | sb.AppendLine( 70 | $" return (IReadOnlyList){groupedAttribute.Key.Name}s;"); 71 | } 72 | 73 | sb.AppendLine("return Array.Empty();"); 74 | sb.AppendLine("}"); 75 | 76 | return sb.ToString(); 77 | } 78 | 79 | internal static string GenerateAttributesInfo(ITypeSymbol classSymbol, ITypeSymbol updateInGroupSymbol) 80 | { 81 | // TODO collect all attributes that can be inherited from base classes 82 | 83 | var attributes = classSymbol.GetAttributes().GroupBy(a => a.AttributeClass, SymbolEqualityComparer.Default) 84 | .ToList(); 85 | var attributesInstances = GenerateAttributesInstances(attributes); 86 | var getFunction = GenerateGetFunction(attributes); 87 | var getListFunction = GenerateGetListFunction(attributes); 88 | 89 | return 90 | $$""" 91 | public class AttributesInfo : AttributesInfoBase 92 | { 93 | public override Type UpdateInGroup { get; } = typeof({{updateInGroupSymbol}}); 94 | 95 | public override AttributesInfoBase GroupMetadata => {{updateInGroupSymbol}}.Metadata; 96 | 97 | {{attributesInstances}} 98 | 99 | {{getFunction}} 100 | 101 | {{getListFunction}} 102 | } 103 | 104 | private static AttributesInfo _attributesInfoCached; 105 | 106 | public static readonly AttributesInfo Metadata = _attributesInfoCached ??= new (); 107 | """; 108 | } 109 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Profile 1": { 4 | "commandName": "DebugRoslynComponent", 5 | "targetProject": "..\\Arch.SystemGroups.Tests\\Arch.SystemGroups.Tests.csproj" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/SourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | 9 | namespace Arch.SystemGroups.SourceGenerator 10 | { 11 | [Generator] 12 | public class SourceGenerator : IIncrementalGenerator 13 | { 14 | private const string UpdateInGroupAttr = "UpdateInGroup"; 15 | private const string UpdateInGroupAttrFullName = "UpdateInGroupAttribute"; 16 | 17 | public void Initialize(IncrementalGeneratorInitializationContext context) 18 | { 19 | // Filter Types marked by [UpdateInGroup], [UpdateAfter], [UpdateBefore] 20 | IncrementalValuesProvider typeDeclarations = context.SyntaxProvider 21 | .CreateSyntaxProvider( 22 | HasUpdateInGroupAttribute, 23 | static (ctx, _) => (TypeDeclarationSyntax) ctx.Node 24 | ); 25 | 26 | // Combine methods with compilation 27 | IncrementalValueProvider<(Compilation, ImmutableArray)> compilationAndTypes = 28 | context.CompilationProvider.Combine(typeDeclarations.WithComparer(TypeComparer.Instance).Collect()); 29 | 30 | context.RegisterSourceOutput(compilationAndTypes, (spc, source) => Generate(source.Item1, source.Item2, spc)); 31 | } 32 | 33 | private static bool HasUpdateInGroupAttribute(SyntaxNode syntaxNode, CancellationToken cancellationToken) 34 | { 35 | if (syntaxNode is not TypeDeclarationSyntax typeDeclarationSyntax) return false; 36 | if (typeDeclarationSyntax.AttributeLists.Count == 0) return false; 37 | 38 | foreach (var attributeListSyntax in typeDeclarationSyntax.AttributeLists) 39 | { 40 | for (var i = 0; i < attributeListSyntax.Attributes.Count; i++) 41 | { 42 | var attr = attributeListSyntax.Attributes[i]; 43 | 44 | string name = null; 45 | switch (attr.Name) 46 | { 47 | case SimpleNameSyntax simpleNameSyntax: 48 | name = simpleNameSyntax.Identifier.Text; 49 | break; 50 | case QualifiedNameSyntax qualifiedNameSyntax: 51 | name = qualifiedNameSyntax.Right.Identifier.Text; 52 | break; 53 | } 54 | 55 | if (name is UpdateInGroupAttr or UpdateInGroupAttrFullName) 56 | return true; 57 | } 58 | } 59 | 60 | return false; 61 | } 62 | 63 | private void Generate(Compilation compilation, ImmutableArray types, 64 | SourceProductionContext context) 65 | { 66 | if (types.IsDefaultOrEmpty) return; 67 | 68 | var worldsInfo = new WorldsInfo(); 69 | 70 | foreach (var typeDeclarationSyntax in types) 71 | { 72 | ITypeSymbol typeSymbol; 73 | try 74 | { 75 | var semanticModel = compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree); 76 | typeSymbol = (ITypeSymbol) ModelExtensions.GetDeclaredSymbol(semanticModel, typeDeclarationSyntax); 77 | } 78 | catch 79 | { 80 | continue; 81 | } 82 | 83 | if (typeSymbol == null) continue; 84 | 85 | var template = PartialClassGenerator.ProcessType(typeSymbol, worldsInfo); 86 | if (template == null) continue; 87 | var fileName = typeSymbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat).Replace('<', '{').Replace('>', '}'); 88 | context.AddSource($"{fileName}.g.cs", CSharpSyntaxTree.ParseText(template).GetRoot().NormalizeWhitespace().ToFullString()); 89 | } 90 | 91 | var allSystemsGen = AddAllSystemsGenerator.GenerateExtensions(worldsInfo); 92 | context.AddSource("SystemWorlds.g.cs", CSharpSyntaxTree.ParseText(allSystemsGen).GetRoot().NormalizeWhitespace().ToFullString()); 93 | } 94 | 95 | private class TypeComparer : IEqualityComparer 96 | { 97 | public static readonly TypeComparer Instance = new(); 98 | 99 | public bool Equals(TypeDeclarationSyntax x, TypeDeclarationSyntax y) 100 | { 101 | return x.Equals(y); 102 | } 103 | 104 | public int GetHashCode(TypeDeclarationSyntax obj) 105 | { 106 | return obj.GetHashCode(); 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/SystemInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Arch.SystemGroups.SourceGenerator 6 | { 7 | public struct SystemInfo 8 | { 9 | /// 10 | /// This type of the class containing the Update Attributes. 11 | /// 12 | public ITypeSymbol This { get; set; } 13 | 14 | /// 15 | /// If the class containing Update Attributes method is within the global namespace. 16 | /// 17 | public bool IsGlobalNamespace { get; set; } 18 | 19 | /// 20 | /// The namespace of the method. 21 | /// 22 | public string Namespace { get; set; } 23 | 24 | /// 25 | /// Public, internal or private 26 | /// 27 | public string AccessModifier { get; set; } 28 | 29 | /// 30 | /// The name of the class containing these Update Attributes. 31 | /// 32 | public string ClassName { get; set; } 33 | 34 | /// 35 | /// Inherits from PlayerLoopSystem 36 | /// 37 | public bool InheritsFromPlayerLoopSystem { get; set; } 38 | 39 | /// 40 | /// Multiple Attributes that are used to define the order of the system. 41 | /// [UpdateBefore(typeof(CustomSystem))] 42 | /// 43 | public IList UpdateBefore { get; set; } 44 | 45 | /// 46 | /// Multiple Attributes that are used to define the order of the system. 47 | /// [UpdateAfter(typeof(CustomSystem))] 48 | /// 49 | public IList UpdateAfter { get; set; } 50 | 51 | /// 52 | /// The type of the SystemGroup or CustomType 53 | /// 54 | public ITypeSymbol UpdateInGroup { get; set; } 55 | 56 | /// 57 | /// Constructor Parameters without the World parameter 58 | /// 59 | public ImmutableArray ConstructorParams { get; set; } 60 | 61 | /// 62 | /// Type of the world if the class implements BaseSystem{W,T}'2 63 | /// 64 | public ITypeSymbol WorldType { get; set; } 65 | 66 | /// 67 | /// Throttling enabled for this system 68 | /// 69 | public bool ThrottlingEnabled { get; set; } 70 | } 71 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.SourceGenerator/WorldInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Arch.SystemGroups.SourceGenerator; 6 | 7 | internal class WorldInfo : IEqualityComparer> 8 | { 9 | internal class GroupByArgs 10 | { 11 | public IList Types { get; set; } 12 | public string PassString { get; set; } 13 | 14 | public string ArgsString { get; set; } 15 | } 16 | 17 | internal ITypeSymbol WorldType { get; } 18 | 19 | internal Dictionary, GroupByArgs> Groups { get; } 20 | 21 | internal void AddSystem(ITypeSymbol systemType, ImmutableArray parameters, string args, string argsPass) 22 | { 23 | if (!Groups.TryGetValue(parameters, out var groupByArgs)) 24 | Groups[parameters] = groupByArgs = new GroupByArgs {Types = new List(), PassString = argsPass, ArgsString = args}; 25 | 26 | groupByArgs.Types.Add(systemType); 27 | } 28 | 29 | public WorldInfo(ITypeSymbol worldType) 30 | { 31 | Groups = new Dictionary, GroupByArgs>(this); 32 | WorldType = worldType; 33 | } 34 | 35 | public bool Equals(ImmutableArray x, ImmutableArray y) 36 | { 37 | if (x.Length != y.Length) 38 | return false; 39 | 40 | for (int i = 0; i < x.Length; i++) 41 | { 42 | if (!AreParameterTypesEqual(x[i], y[i])) 43 | return false; 44 | } 45 | 46 | return true; 47 | } 48 | 49 | public int GetHashCode(ImmutableArray parameters) 50 | { 51 | unchecked 52 | { 53 | int hash = 17; 54 | 55 | foreach (var parameter in parameters) 56 | { 57 | hash = hash * 23 + (parameter.Type?.GetHashCode() ?? 0); 58 | } 59 | 60 | return hash; 61 | } 62 | } 63 | 64 | private bool AreParameterTypesEqual(IParameterSymbol parameter1, IParameterSymbol parameter2) 65 | { 66 | if (ReferenceEquals(parameter1, parameter2)) 67 | return true; 68 | 69 | if (parameter1 is null || parameter2 is null) 70 | return false; 71 | 72 | // Compare only the types of the parameters 73 | return parameter1.Type.Equals(parameter2.Type, SymbolEqualityComparer.Default); 74 | } 75 | } 76 | 77 | internal class WorldsInfo 78 | { 79 | internal Dictionary Worlds { get; } = new(SymbolEqualityComparer.Default); 80 | 81 | public WorldInfo GetWorldInfo(ITypeSymbol type) => Worlds.TryGetValue(type, out var world) 82 | ? world 83 | : Worlds[type] = new WorldInfo(type); 84 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/Arch.SystemGroups.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 10 11 | 12 | 13 | 14 | TRACE; 15 | 16 | 17 | 18 | TRACE; 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/AssertHelpers.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | 3 | namespace Arch.SystemGroups.Tests; 4 | 5 | public static class AssertHelpers 6 | { 7 | /// System or Group Type 8 | internal static int FindIndexOfNode(this List> list) 9 | { 10 | return list.FindIndex(n => n.IsGroup ? n.CustomGroup.GetType() == typeof(T) : n.System.GetType() == typeof(T)); 11 | } 12 | 13 | public static void AssertOrderOfSystemIsLessThenOtherSystem(SystemGroupWorld world) 14 | where TGroup : SystemGroup 15 | { 16 | var group = world.SystemGroups.OfType().First(); 17 | var index1 = group.Nodes.FindIndexOfNode(); 18 | var index2 = group.Nodes.FindIndexOfNode(); 19 | 20 | if (index1 == -1) 21 | throw new InvalidOperationException($"System {typeof(T1)} not found in group {typeof(TGroup)}"); 22 | if (index2 == -1) 23 | throw new InvalidOperationException($"System {typeof(T2)} not found in group {typeof(TGroup)}"); 24 | 25 | Assert.That(index1, Is.LessThan(index2)); 26 | } 27 | 28 | internal static void AssertOrderOfSystemIsLessThenOtherSystem(List> nodes) 29 | { 30 | var index1 = nodes.FindIndexOfNode(); 31 | var index2 = nodes.FindIndexOfNode(); 32 | 33 | if (index1 == -1) 34 | throw new InvalidOperationException($"System {typeof(T1)} not found)"); 35 | 36 | if (index2 == -1) 37 | throw new InvalidOperationException($"System {typeof(T2)} not found)"); 38 | 39 | Assert.That(index1, Is.LessThan(index2)); 40 | } 41 | 42 | internal static void AssertNodesEquivalency(List> nodes, params Type[] expectedTypes) 43 | { 44 | CollectionAssert.AreEquivalent(expectedTypes, nodes.Select(n => n.IsGroup ? n.CustomGroup.GetType() : n.System.GetType())); 45 | } 46 | 47 | internal static void AssertNodesEquality(List> nodes, params Type[] expectedTypes) 48 | { 49 | CollectionAssert.AreEqual(expectedTypes, nodes.Select(n => n.IsGroup ? n.CustomGroup.GetType() : n.System.GetType())); 50 | } 51 | 52 | internal static T Find(this List> nodes) 53 | { 54 | foreach (var executionNode in nodes) 55 | { 56 | if (executionNode is { IsGroup: true, CustomGroup: T group }) 57 | return group; 58 | 59 | if (executionNode is { IsGroup: false, System: T system }) 60 | return system; 61 | } 62 | 63 | throw new InvalidOperationException($"System or Group {typeof(T)} not found"); 64 | } 65 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/CentralizedThrottlingTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | using Arch.SystemGroups.Throttling; 5 | using Arch.SystemGroups.UnityBridge; 6 | using NSubstitute; 7 | 8 | namespace Arch.SystemGroups.Tests.CentralizedThrottling; 9 | 10 | public class CentralizedThrottlingTests 11 | { 12 | private NoThrottlingRootSystem _noThrottlingRootSystem; 13 | private ThrottlingRootSystem _throttlingRootSystem; 14 | private ImplicitThrottlingExplicitNestedSystem _implicitThrottlingExplicitNestedSystem; 15 | private ImplicitThrottlingImplicitNestedSystem _implicitThrottlingImplicitNestedSystem; 16 | private NoThrottlingNestedSystem _noThrottlingNestedSystem; 17 | 18 | private IUpdateBasedSystemGroupThrottler _throttler; 19 | 20 | private ArchSystemsWorldBuilder _worldBuilder; 21 | 22 | [SetUp] 23 | public void SetUp() 24 | { 25 | _throttler = Substitute.For(); 26 | 27 | _worldBuilder = new ArchSystemsWorldBuilder(new TestWorld(), Substitute.For(), updateBasedSystemGroupThrottler: _throttler); 28 | 29 | _noThrottlingRootSystem = NoThrottlingRootSystem.InjectToWorld(ref _worldBuilder); 30 | _throttlingRootSystem = ThrottlingRootSystem.InjectToWorld(ref _worldBuilder); 31 | _implicitThrottlingExplicitNestedSystem = ImplicitThrottlingExplicitNestedSystem.InjectToWorld(ref _worldBuilder); 32 | _implicitThrottlingImplicitNestedSystem = ImplicitThrottlingImplicitNestedSystem.InjectToWorld(ref _worldBuilder); 33 | _noThrottlingNestedSystem = NoThrottlingNestedSystem.InjectToWorld(ref _worldBuilder); 34 | } 35 | 36 | [Test] 37 | public void UpdatesAllSystemIfNotThrottled() 38 | { 39 | _throttler.ShouldThrottle(Arg.Any(), Arg.Any()).Returns(false); 40 | 41 | var world = _worldBuilder.Finish(); 42 | world.Initialize(); 43 | 44 | world.SystemGroups.OfType().First().Update(); 45 | 46 | AssertSystemUpdatesInvoked(_noThrottlingNestedSystem); 47 | AssertSystemUpdatesInvoked(_implicitThrottlingExplicitNestedSystem); 48 | AssertSystemUpdatesInvoked(_implicitThrottlingImplicitNestedSystem); 49 | AssertSystemUpdatesInvoked(_noThrottlingRootSystem); 50 | AssertSystemUpdatesInvoked(_throttlingRootSystem); 51 | } 52 | 53 | [Test] 54 | public void UpdatesNoThrottlingSystemsIfThrottled() 55 | { 56 | _throttler.ShouldThrottle(Arg.Any(), Arg.Any()).Returns(true); 57 | 58 | var world = _worldBuilder.Finish(); 59 | world.Initialize(); 60 | 61 | world.SystemGroups.OfType().First().Update(); 62 | 63 | AssertSystemUpdatesInvoked(_noThrottlingNestedSystem); 64 | AssertSystemUpdatesInvoked(_noThrottlingRootSystem); 65 | } 66 | 67 | [Test] 68 | public void DoesNotUpdateThrottlingSystemsIfThrottled() 69 | { 70 | _throttler.ShouldThrottle(Arg.Any(), Arg.Any()).Returns(true); 71 | 72 | var world = _worldBuilder.Finish(); 73 | 74 | world.SystemGroups.OfType().First().Update(); 75 | 76 | AssertSystemUpdatesNotInvoked(_implicitThrottlingExplicitNestedSystem); 77 | AssertSystemUpdatesNotInvoked(_implicitThrottlingImplicitNestedSystem); 78 | AssertSystemUpdatesNotInvoked(_throttlingRootSystem); 79 | } 80 | 81 | private void AssertSystemUpdatesNotInvoked(CentralizedThrottlingTestSystemBase system) 82 | { 83 | Assert.Multiple(() => 84 | { 85 | Assert.That(system.UpdateInvoked, Is.False); 86 | Assert.That(system.AfterUpdateInvoked, Is.False); 87 | Assert.That(system.BeforeUpdateInvoked, Is.False); 88 | }); 89 | } 90 | 91 | private void AssertSystemUpdatesInvoked(CentralizedThrottlingTestSystemBase system) 92 | { 93 | Assert.Multiple(() => 94 | { 95 | Assert.That(system.UpdateInvoked, Is.True); 96 | Assert.That(system.AfterUpdateInvoked, Is.True); 97 | Assert.That(system.BeforeUpdateInvoked, Is.True); 98 | }); 99 | } 100 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Groups/ImplicitThrottlingNestedGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 2 | 3 | [UpdateInGroup(typeof(ThrottlingRootGroup))] 4 | public partial class ImplicitThrottlingNestedGroup 5 | { 6 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Groups/NoThrottlingRootGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 4 | 5 | [UpdateInGroup(typeof(SimulationSystemGroup))] 6 | public partial class NoThrottlingRootGroup 7 | { 8 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Groups/ThrottlingNestedGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.Throttling; 2 | 3 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 4 | 5 | [UpdateInGroup(typeof(NoThrottlingRootGroup))] 6 | [ThrottlingEnabled] 7 | public partial class ThrottlingNestedGroup 8 | { 9 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Groups/ThrottlingRootGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Throttling; 3 | 4 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 5 | 6 | [ThrottlingEnabled] 7 | [UpdateInGroup(typeof(SimulationSystemGroup))] 8 | public partial class ThrottlingRootGroup 9 | { 10 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Systems/CentralizedThrottlingTestSystemBase.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 5 | 6 | public abstract class CentralizedThrottlingTestSystemBase : BaseSystem 7 | { 8 | public bool UpdateInvoked { get; private set; } 9 | public bool BeforeUpdateInvoked { get; private set; } 10 | public bool AfterUpdateInvoked { get; private set; } 11 | 12 | protected CentralizedThrottlingTestSystemBase(TestWorld world) : base(world) 13 | { 14 | } 15 | 16 | public override void Update(in float t) 17 | { 18 | UpdateInvoked = true; 19 | } 20 | 21 | public override void BeforeUpdate(in float t) 22 | { 23 | BeforeUpdateInvoked = true; 24 | } 25 | 26 | public override void AfterUpdate(in float t) 27 | { 28 | AfterUpdateInvoked = true; 29 | } 30 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Systems/ImplicitThrottlingExplicitNestedSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 6 | 7 | [UpdateInGroup(typeof(ThrottlingNestedGroup))] 8 | public partial class ImplicitThrottlingExplicitNestedSystem : CentralizedThrottlingTestSystemBase 9 | { 10 | public ImplicitThrottlingExplicitNestedSystem(TestWorld world) : base(world) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Systems/ImplicitThrottlingImplicitNestedSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 5 | 6 | [UpdateInGroup(typeof(ImplicitThrottlingNestedGroup))] 7 | public partial class ImplicitThrottlingImplicitNestedSystem : CentralizedThrottlingTestSystemBase 8 | { 9 | public ImplicitThrottlingImplicitNestedSystem(TestWorld world) : base(world) 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Systems/NoThrottlingNestedSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.CentralizedThrottling.Groups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 6 | 7 | [UpdateInGroup(typeof(NoThrottlingRootGroup))] 8 | public partial class NoThrottlingNestedSystem : CentralizedThrottlingTestSystemBase 9 | { 10 | public NoThrottlingNestedSystem(TestWorld world) : base(world) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Systems/NoThrottlingRootSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 6 | 7 | [UpdateInGroup(typeof(SimulationSystemGroup))] 8 | public partial class NoThrottlingRootSystem : CentralizedThrottlingTestSystemBase 9 | { 10 | public NoThrottlingRootSystem(TestWorld world) : base(world) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/CentralizedThrottling/Systems/ThrottlingRootSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | using Arch.SystemGroups.Throttling; 4 | 5 | namespace Arch.SystemGroups.Tests.CentralizedThrottling.Systems; 6 | 7 | [UpdateInGroup(typeof(SimulationSystemGroup))] 8 | [ThrottlingEnabled] 9 | public partial class ThrottlingRootSystem : CentralizedThrottlingTestSystemBase 10 | { 11 | internal ThrottlingRootSystem(TestWorld world) : base(world) 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/DescriptorTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Descriptors; 3 | using Arch.SystemGroups.Tests.NestedGroups; 4 | using Arch.SystemGroups.Tests.TestSetup1; 5 | using NSubstitute; 6 | 7 | namespace Arch.SystemGroups.Tests.DescriptorTests; 8 | 9 | public class DescriptorTests 10 | { 11 | private ArchSystemsWorldBuilder _worldBuilder; 12 | 13 | [SetUp] 14 | public void SetUp() 15 | { 16 | _worldBuilder = new ArchSystemsWorldBuilder(new TestWorld(), Substitute.For()); 17 | SystemGroups.InitSystemGroupSystemTest.InjectToWorld(ref _worldBuilder); 18 | PhysicsSystemGroupSystemTest.InjectToWorld(ref _worldBuilder); 19 | PostPhysicsSystemGroupSystemTest.InjectToWorld(ref _worldBuilder); 20 | PostRenderingSystemGroupSystemTest.InjectToWorld(ref _worldBuilder); 21 | PresentationSystemGroupSystemTest.InjectToWorld(ref _worldBuilder); 22 | SimulationSystemGroupSystemTest.InjectToWorld(ref _worldBuilder); 23 | } 24 | 25 | [Test] 26 | [TestCase(nameof(InitializationSystemGroup))] 27 | [TestCase(nameof(PhysicsSystemGroup))] 28 | [TestCase(nameof(PostPhysicsSystemGroup))] 29 | [TestCase(nameof(PreRenderingSystemGroup))] 30 | [TestCase(nameof(PostRenderingSystemGroup))] 31 | [TestCase(nameof(SimulationSystemGroup))] 32 | public void CreatesSystemDescriptorInEachGroup(string group) 33 | { 34 | var systemGroupWorld = _worldBuilder.Finish(); 35 | var descriptor = systemGroupWorld.GenerateDescriptors(); 36 | Assert.That(descriptor.Where(x => x.Name == group).Count(), Is.EqualTo(1)); 37 | } 38 | 39 | [Test] 40 | public void CreatesAllSystemDescriptors() 41 | { 42 | var systemGroupWorld = _worldBuilder.Finish(); 43 | var descriptor = systemGroupWorld.GenerateDescriptors(); 44 | Assert.That(descriptor.Count, Is.EqualTo(7)); 45 | } 46 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/NestedDescriptorTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Descriptors; 3 | using Arch.SystemGroups.Tests.NestedGroups; 4 | using Arch.SystemGroups.Tests.TestSetup1; 5 | using NSubstitute; 6 | 7 | namespace Arch.SystemGroups.Tests.DescriptorTests; 8 | 9 | public class NestedDescriptorTests 10 | { 11 | private ArchSystemsWorldBuilder _worldBuilder; 12 | 13 | [SetUp] 14 | public void SetUp() 15 | { 16 | _worldBuilder = new ArchSystemsWorldBuilder(new TestWorld(), Substitute.For()); 17 | SystemInNestedGroup.InjectToWorld(ref _worldBuilder); 18 | } 19 | [Test] 20 | public void NestedSystemGroupsAreCreated() 21 | { 22 | var systemGroupWorld = _worldBuilder.Finish(); 23 | var simulationSystemGroup = 24 | systemGroupWorld.GenerateDescriptors().FirstOrDefault(x => x.Name == nameof(SimulationSystemGroup)); 25 | var root = simulationSystemGroup.SubDescriptors.First(); 26 | var firstLevel = root.SubDescriptors.First(); 27 | var secondLevel = firstLevel.SubDescriptors.First(); 28 | 29 | Assert.That(root.Name, Is.EqualTo(nameof(RootGroup))); 30 | Assert.True(root.IsGroup); 31 | 32 | Assert.That(firstLevel.Name, Is.EqualTo(nameof(NestedGroup1))); 33 | Assert.True(firstLevel.IsGroup); 34 | 35 | Assert.That(secondLevel.Name, Is.EqualTo(nameof(NestedGroup2))); 36 | Assert.True(secondLevel.IsGroup); 37 | 38 | Assert.True(secondLevel.SubDescriptors.First().IsSystem); 39 | Assert.That(secondLevel.SubDescriptors.First().Name, Is.EqualTo(nameof(SystemInNestedGroup))); 40 | } 41 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/SystemGroups/InitSystemGroupSystemTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DescriptorTests.SystemGroups 6 | { 7 | [UpdateInGroup(typeof(InitializationSystemGroup))] 8 | public partial class InitSystemGroupSystemTest : BaseSystem 9 | { 10 | public InitSystemGroupSystemTest(TestWorld world) : base(world) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/SystemGroups/PhysicsSystemGroupSystemTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DescriptorTests 6 | { 7 | [UpdateInGroup(typeof(PhysicsSystemGroup))] 8 | public partial class PhysicsSystemGroupSystemTest : BaseSystem 9 | { 10 | public PhysicsSystemGroupSystemTest(TestWorld world) : base(world) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/SystemGroups/PostPhysicsSystemGroupSystemTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DescriptorTests 6 | { 7 | [UpdateInGroup(typeof(PostPhysicsSystemGroup))] 8 | public partial class PostPhysicsSystemGroupSystemTest : BaseSystem 9 | { 10 | public PostPhysicsSystemGroupSystemTest(TestWorld world) : base(world) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/SystemGroups/PostRenderingSystemGroupSystemTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DescriptorTests 6 | { 7 | [UpdateInGroup(typeof(PostRenderingSystemGroup))] 8 | public partial class PostRenderingSystemGroupSystemTest : BaseSystem 9 | { 10 | public PostRenderingSystemGroupSystemTest(TestWorld world) : base(world) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/SystemGroups/PresentationSystemGroupSystemTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DescriptorTests 6 | { 7 | [UpdateInGroup(typeof(PresentationSystemGroup))] 8 | public partial class PresentationSystemGroupSystemTest : BaseSystem 9 | { 10 | public PresentationSystemGroupSystemTest(TestWorld world) : base(world) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DescriptorTests/SystemGroups/SimulationSystemGroupSystemTest.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DescriptorTests 6 | { 7 | [UpdateInGroup(typeof(SimulationSystemGroup))] 8 | public partial class SimulationSystemGroupSystemTest : BaseSystem 9 | { 10 | public SimulationSystemGroupSystemTest(TestWorld world) : base(world) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DisconnectedDependencies/DisconnectedDependenciesTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.Tests.TestSetup1; 2 | using NSubstitute; 3 | 4 | namespace Arch.SystemGroups.Tests.DisconnectedDependencies; 5 | 6 | public class DisconnectedDependenciesTests 7 | { 8 | private ArchSystemsWorldBuilder _worldBuilder; 9 | 10 | [SetUp] 11 | public void SetUp() 12 | { 13 | _worldBuilder = new ArchSystemsWorldBuilder(new TestWorld(), Substitute.For()); 14 | } 15 | 16 | [Test] 17 | public void ThrowsOnSystems() 18 | { 19 | DDSystem1.InjectToWorld(ref _worldBuilder); 20 | DDSystem2.InjectToWorld(ref _worldBuilder); 21 | 22 | Assert.Throws(() => _worldBuilder.Finish()); 23 | } 24 | 25 | [Test] 26 | public void ThrowsOnGroups() 27 | { 28 | DDSystem1Gr1.InjectToWorld(ref _worldBuilder); 29 | DDSystem1Gr2.InjectToWorld(ref _worldBuilder); 30 | 31 | Assert.Throws(() => _worldBuilder.Finish()); 32 | } 33 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DisconnectedDependencies/Groups.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.DisconnectedDependencies; 4 | 5 | [UpdateInGroup(typeof(PresentationSystemGroup))] 6 | public partial class DDGroup1 7 | { 8 | 9 | } 10 | 11 | [UpdateInGroup(typeof(SimulationSystemGroup))] 12 | [UpdateBefore(typeof(DDGroup1))] 13 | public partial class DDGroup2 14 | { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/DisconnectedDependencies/Systems.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.DisconnectedDependencies; 6 | 7 | [UpdateInGroup(typeof(PostRenderingSystemGroup))] 8 | [UpdateAfter(typeof(DDSystem2))] // exception 9 | public partial class DDSystem1 : BaseSystem 10 | { 11 | public DDSystem1(TestWorld world) : base(world) 12 | { 13 | } 14 | } 15 | 16 | [UpdateInGroup(typeof(SimulationSystemGroup))] 17 | public partial class DDSystem2 : BaseSystem 18 | { 19 | public DDSystem2(TestWorld world) : base(world) 20 | { 21 | } 22 | } 23 | 24 | [UpdateInGroup(typeof(DDGroup1))] 25 | public partial class DDSystem1Gr1 : BaseSystem 26 | { 27 | public DDSystem1Gr1(TestWorld world) : base(world) 28 | { 29 | } 30 | } 31 | 32 | [UpdateInGroup(typeof(DDGroup2))] 33 | public partial class DDSystem1Gr2 : BaseSystem 34 | { 35 | public DDSystem1Gr2(TestWorld world) : base(world) 36 | { 37 | } 38 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ExceptionsHandling/ExceptionsHandlingTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | using NSubstitute; 4 | 5 | namespace Arch.SystemGroups.Tests.ExceptionsHandling; 6 | 7 | public class ExceptionsHandlingTests 8 | { 9 | private ThrowingSystem1 _throwingSystem1; 10 | 11 | private ISystemGroupExceptionHandler _exceptionHandler; 12 | private ArchSystemsWorldBuilder _worldBuilder; 13 | private IPlayerLoop _loop; 14 | private SystemGroupWorld _world; 15 | private SystemGroup _systemGroup; 16 | 17 | [SetUp] 18 | public void SetUp() 19 | { 20 | _exceptionHandler = Substitute.For(); 21 | 22 | _worldBuilder = 23 | new ArchSystemsWorldBuilder(new TestWorld(), 24 | _loop = Substitute.For(), exceptionHandler: _exceptionHandler); 25 | 26 | _throwingSystem1 = ThrowingSystem1.InjectToWorld(ref _worldBuilder); 27 | _world = _worldBuilder.Finish(); 28 | 29 | _systemGroup = _world.SystemGroups.OfType().First(); 30 | _systemGroup.Initialize(); 31 | } 32 | 33 | [Test] 34 | public void KeepsSystemsRunning() 35 | { 36 | _exceptionHandler.Handle(Arg.Any(), Arg.Any()) 37 | .Returns(_ => ISystemGroupExceptionHandler.Action.Continue); 38 | 39 | Assert.That(_systemGroup.Update, Throws.Nothing); 40 | } 41 | 42 | [Test] 43 | public void Rethrows() 44 | { 45 | _exceptionHandler.Handle(Arg.Any(), Arg.Any()) 46 | .Returns(c => throw c.Arg()); 47 | 48 | Assert.That(_systemGroup.Update, Throws.InstanceOf()); 49 | } 50 | 51 | [Test] 52 | public void Suspends() 53 | { 54 | _exceptionHandler.Handle(Arg.Any(), Arg.Any()) 55 | .Returns(_ => ISystemGroupExceptionHandler.Action.Suspend); 56 | 57 | _systemGroup.Update(); 58 | Assert.That(_systemGroup.CurrentState, Is.EqualTo(SystemGroup.State.Suspended)); 59 | } 60 | 61 | [Test] 62 | public void Disposes() 63 | { 64 | _exceptionHandler.Handle(Arg.Any(), Arg.Any()) 65 | .Returns(_ => ISystemGroupExceptionHandler.Action.Dispose); 66 | 67 | _systemGroup.Update(); 68 | Assert.That(_systemGroup.CurrentState, Is.EqualTo(SystemGroup.State.Disposed)); 69 | } 70 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ExceptionsHandling/Systems.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.ExceptionsHandling; 6 | 7 | [UpdateInGroup(typeof(SimulationSystemGroup))] 8 | public partial class ThrowingSystem1 : BaseSystem 9 | { 10 | public ThrowingSystem1(TestWorld world) : base(world) 11 | { 12 | } 13 | 14 | public override void Update(in float t) 15 | { 16 | throw new NotSupportedException(); 17 | } 18 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/GenericSetupTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | using Arch.SystemGroups.Tests.TestSetup1.Systems; 5 | using NSubstitute; 6 | 7 | namespace Arch.SystemGroups.Tests.GenericSetup; 8 | 9 | public class GenericSetupTests 10 | { 11 | public struct CustomStruct1 12 | { 13 | } 14 | 15 | public struct CustomEquatableStruct : IEquatable 16 | { 17 | public int A; 18 | 19 | public bool Equals(CustomEquatableStruct other) 20 | { 21 | return A == other.A; 22 | } 23 | 24 | public override bool Equals(object? obj) 25 | { 26 | return obj is CustomEquatableStruct other && Equals(other); 27 | } 28 | 29 | public override int GetHashCode() 30 | { 31 | return A; 32 | } 33 | } 34 | 35 | public enum CustomEnum1 36 | { 37 | A, B, C, D 38 | } 39 | 40 | private ArchSystemsWorldBuilder _worldBuilder; 41 | private IPlayerLoop? _loopHelper; 42 | 43 | private GenericSystem0 _genericSystem0; 44 | 45 | private GenericSystem1 _genericSystem1Int; 46 | private GenericSystem1 _genericSystem1String; 47 | 48 | private GenericSystem2 _genericSystem2CustomStruct1; 49 | private GenericSystem2 _genericSystem2Float; 50 | private GenericSystem2 _genericSystem2Double; 51 | 52 | private GenericSystem3 _genericSystem3ObjectCustomStruct1TestEnum; 53 | 54 | [SetUp] 55 | public void SetUp() 56 | { 57 | _worldBuilder = new ArchSystemsWorldBuilder(new TestWorld(), _loopHelper = Substitute.For()); 58 | 59 | _genericSystem0 = GenericSystem0.InjectToWorld(ref _worldBuilder, 0.5d); 60 | 61 | _genericSystem1Int = GenericSystem1.InjectToWorld(ref _worldBuilder, 1); 62 | _genericSystem1String = GenericSystem1.InjectToWorld(ref _worldBuilder, "test"); 63 | 64 | _genericSystem2CustomStruct1 = GenericSystem2.InjectToWorld(ref _worldBuilder, new CustomStruct1()); 65 | _genericSystem2Float = GenericSystem2.InjectToWorld(ref _worldBuilder, 1.0f); 66 | _genericSystem2Double = GenericSystem2.InjectToWorld(ref _worldBuilder, 1.0); 67 | 68 | _genericSystem3ObjectCustomStruct1TestEnum = GenericSystem3.InjectToWorld(ref _worldBuilder, new object(), new CustomEquatableStruct {A = 100}, CustomEnum1.B); 69 | } 70 | 71 | [Test] 72 | public void BuildsHierarchy() 73 | { 74 | var world = _worldBuilder.Finish(); 75 | 76 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(world); 77 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem, Group1OfGenerics>(world); 78 | 79 | var init = world.SystemGroups.OfType().First(); 80 | var group1 = init.Nodes.Find(); 81 | var group2 = init.Nodes.Find(); 82 | 83 | CollectionAssert.AreEquivalent(new List> 84 | { 85 | _genericSystem3ObjectCustomStruct1TestEnum 86 | }, group1.Nodes.Select(n => n.System)); 87 | 88 | CollectionAssert.AreEquivalent(new List> 89 | { 90 | _genericSystem1Int, 91 | _genericSystem2Float, 92 | _genericSystem2Double, 93 | _genericSystem1String, 94 | _genericSystem2CustomStruct1 95 | }, group2.Nodes.Select(n => n.System)); 96 | } 97 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/GenericSystem0.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.GenericSetup; 6 | 7 | [UpdateInGroup(typeof(InitializationSystemGroup))] 8 | [UpdateBefore(typeof(Group1OfGenerics))] 9 | public partial class GenericSystem0 : BaseSystem where T : struct 10 | { 11 | private readonly T _param1; 12 | 13 | public GenericSystem0(TestWorld world, T param1) : base(world) 14 | { 15 | _param1 = param1; 16 | } 17 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/GenericSystem1.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.GenericSetup; 5 | 6 | [UpdateInGroup(typeof(Group2OfGenerics))] 7 | public partial class GenericSystem1 : BaseSystem 8 | { 9 | private readonly T _param1; 10 | 11 | public GenericSystem1(TestWorld world, T param1) : base(world) 12 | { 13 | _param1 = param1; 14 | } 15 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/GenericSystem2.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.GenericSetup; 6 | 7 | [UpdateInGroup(typeof(Group2OfGenerics))] 8 | public partial class GenericSystem2 : BaseSystem where TStruct : struct 9 | { 10 | private readonly TStruct _param1; 11 | 12 | public GenericSystem2(TestWorld world, TStruct param1) : base(world) 13 | { 14 | _param1 = param1; 15 | } 16 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/GenericSystem3.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.GenericSetup; 6 | 7 | [UpdateInGroup(typeof(Group1OfGenerics))] 8 | public partial class GenericSystem3 : BaseSystem 9 | where T1 : class where T2: IEquatable, new() where T3 : unmanaged, Enum 10 | { 11 | private readonly T1 _t1; 12 | private readonly T2 _t2; 13 | private readonly T3 _en; 14 | 15 | public GenericSystem3(TestWorld world, T1 t1, T2 t2, T3 en) : base(world) 16 | { 17 | _t1 = t1; 18 | _t2 = t2; 19 | _en = en; 20 | } 21 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/Group1OfGenerics.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.GenericSetup; 4 | 5 | [UpdateInGroup(typeof(InitializationSystemGroup))] 6 | public partial class Group1OfGenerics {} -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/Group2OfGenerics.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.GenericSetup; 4 | 5 | [UpdateInGroup(typeof(InitializationSystemGroup))] 6 | [UpdateAfter(typeof(Group1OfGenerics))] 7 | public partial class Group2OfGenerics {} -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/GenericSetup/NestedArgument.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.GenericSetup; 6 | 7 | public class ParentClass 8 | { 9 | public class NestedType 10 | { 11 | public T Value; 12 | } 13 | } 14 | 15 | public struct ParamGen 16 | { 17 | public bool Value; 18 | } 19 | 20 | [UpdateInGroup(typeof(SimulationSystemGroup))] 21 | public partial class SystemWithGenericNestedArgument : BaseSystem 22 | { 23 | private readonly ParentClass.NestedType _param; 24 | 25 | internal SystemWithGenericNestedArgument(TestWorld world, ParentClass.NestedType param) : base(world) 26 | { 27 | _param = param; 28 | } 29 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Custom1Attribute.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.MetadataTests; 2 | 3 | [AttributeUsage(AttributeTargets.Class)] 4 | public class Custom1Attribute : Attribute, IEquatable 5 | { 6 | public readonly string StrValue; 7 | 8 | public Custom1Attribute(string str) 9 | { 10 | StrValue = str; 11 | } 12 | 13 | public bool Equals(Custom1Attribute? other) 14 | { 15 | if (ReferenceEquals(null, other)) return false; 16 | if (ReferenceEquals(this, other)) return true; 17 | return base.Equals(other) && StrValue == other.StrValue; 18 | } 19 | 20 | public override bool Equals(object? obj) 21 | { 22 | if (ReferenceEquals(null, obj)) return false; 23 | if (ReferenceEquals(this, obj)) return true; 24 | if (obj.GetType() != this.GetType()) return false; 25 | return Equals((Custom1Attribute)obj); 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | return HashCode.Combine(base.GetHashCode(), StrValue); 31 | } 32 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Custom2Attribute.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.MetadataTests; 2 | 3 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 4 | public class Custom2Attribute : Attribute, IEquatable 5 | { 6 | public readonly int V1; 7 | public readonly float V2; 8 | 9 | public Custom2Attribute(int v1, float v2) 10 | { 11 | V1 = v1; 12 | V2 = v2; 13 | } 14 | 15 | public bool Equals(Custom2Attribute? other) 16 | { 17 | if (ReferenceEquals(null, other)) return false; 18 | if (ReferenceEquals(this, other)) return true; 19 | return base.Equals(other) && V1 == other.V1 && V2.Equals(other.V2); 20 | } 21 | 22 | public override bool Equals(object? obj) 23 | { 24 | if (ReferenceEquals(null, obj)) return false; 25 | if (ReferenceEquals(this, obj)) return true; 26 | if (obj.GetType() != this.GetType()) return false; 27 | return Equals((Custom2Attribute)obj); 28 | } 29 | 30 | public override int GetHashCode() 31 | { 32 | return HashCode.Combine(base.GetHashCode(), V1, V2); 33 | } 34 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Custom3Attribute.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.MetadataTests; 2 | 3 | [AttributeUsage(AttributeTargets.Class)] 4 | public class Custom3Attribute : Attribute 5 | { 6 | public readonly Type Type; 7 | 8 | public Custom3Attribute(Type type) 9 | { 10 | Type = type; 11 | } 12 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Custom4Attribute.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.MetadataTests; 2 | 3 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 4 | public class Custom4Attribute : Attribute, IEquatable 5 | { 6 | public readonly string[] Array; 7 | 8 | public Custom4Attribute(params string[] array) 9 | { 10 | Array = array; 11 | } 12 | 13 | public bool Equals(Custom4Attribute? other) 14 | { 15 | if (ReferenceEquals(null, other)) return false; 16 | if (ReferenceEquals(this, other)) return true; 17 | return base.Equals(other) && Array.SequenceEqual(other.Array); 18 | } 19 | 20 | public override bool Equals(object? obj) 21 | { 22 | if (ReferenceEquals(null, obj)) return false; 23 | if (ReferenceEquals(this, obj)) return true; 24 | if (obj.GetType() != GetType()) return false; 25 | return Equals((Custom4Attribute)obj); 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | return HashCode.Combine(base.GetHashCode(), Array); 31 | } 32 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Groups/MetadataGroup1.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.MetadataTests.Groups; 4 | 5 | [UpdateInGroup(typeof(SimulationSystemGroup))] 6 | [Custom3(typeof(string))] 7 | [Custom4("TestValue1", "TestValue2")] 8 | public partial class MetadataGroup1 9 | { 10 | 11 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Groups/MetadataNestedGroup1.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.MetadataTests.Groups; 2 | 3 | [UpdateInGroup(typeof(MetadataGroup1))] 4 | [Custom1("MetadataNestedGroup1")] 5 | public partial class MetadataNestedGroup1 6 | { 7 | 8 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/MetadataTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Metadata; 3 | using Arch.SystemGroups.Tests.MetadataTests.Groups; 4 | using Arch.SystemGroups.Tests.MetadataTests.Systems; 5 | 6 | namespace Arch.SystemGroups.Tests.MetadataTests; 7 | 8 | public class MetadataTests 9 | { 10 | [Test] 11 | public void System1ProvidesAttributes() 12 | { 13 | var metadata = MetadataSystem1.Metadata; 14 | 15 | Assert.That(metadata, Is.Not.Null); 16 | 17 | var custom1 = metadata.GetAttributes(); 18 | Assert.That(custom1, Is.Not.Null); 19 | Assert.That(custom1.Count, Is.EqualTo(1)); 20 | Assert.That(custom1[0].StrValue, Is.EqualTo("TestValue1")); 21 | 22 | var custom2 = metadata.GetAttributes(); 23 | 24 | CollectionAssert.AreEquivalent(new Custom2Attribute[] 25 | { 26 | new(100, 200.5f), 27 | new(1, 2f) 28 | }, custom2); 29 | 30 | Assert.That(metadata.UpdateInGroup, Is.EqualTo(typeof(SimulationSystemGroup))); 31 | Assert.That(metadata.GroupMetadata, Is.EqualTo(SystemGroupAttributesInfo.Instance)); 32 | } 33 | 34 | [Test] 35 | public void System2ProvidesAttributes() 36 | { 37 | var metadata = MetadataSystem2.Metadata; 38 | 39 | Assert.That(metadata, Is.Not.Null); 40 | 41 | var custom1 = metadata.GetAttributes(); 42 | Assert.That(custom1, Is.Not.Null); 43 | Assert.That(custom1.Count, Is.EqualTo(1)); 44 | Assert.That(custom1[0].StrValue, Is.EqualTo("TestValue2")); 45 | 46 | var custom2 = metadata.GetAttributes(); 47 | 48 | CollectionAssert.AreEquivalent(new Custom2Attribute[] 49 | { 50 | new(0, 0f), 51 | new(1, 1f), 52 | new(2, 2f), 53 | new(3, 3f) 54 | }, custom2); 55 | 56 | Assert.That(metadata.UpdateInGroup, Is.EqualTo(typeof(MetadataGroup1))); 57 | Assert.That(metadata.GroupMetadata, Is.EqualTo(MetadataGroup1.Metadata)); 58 | } 59 | 60 | [Test] 61 | public void Group1ProvidesAttributes() 62 | { 63 | var metadata = MetadataGroup1.Metadata; 64 | 65 | Assert.That(metadata, Is.Not.Null); 66 | 67 | var custom3 = metadata.GetAttributes(); 68 | Assert.That(custom3, Is.Not.Null); 69 | Assert.That(custom3.Count, Is.EqualTo(1)); 70 | Assert.That(custom3[0].Type, Is.EqualTo(typeof(string))); 71 | 72 | var custom4 = metadata.GetAttributes(); 73 | 74 | CollectionAssert.AreEquivalent(new Custom4Attribute[] 75 | { 76 | new("TestValue1", "TestValue2") 77 | }, custom4); 78 | 79 | Assert.That(metadata.UpdateInGroup, Is.EqualTo(typeof(SimulationSystemGroup))); 80 | Assert.That(metadata.GroupMetadata, Is.EqualTo(SystemGroupAttributesInfo.Instance)); 81 | } 82 | 83 | [Test] 84 | public void NestedGroup1ProvidesAttributes() 85 | { 86 | CollectionAssert.AreEquivalent(new[] { new Custom1Attribute("MetadataNestedGroup1") }, 87 | MetadataNestedGroup1.AttributesInfo.Custom1Attributes); 88 | 89 | Assert.That(MetadataNestedGroup1.Metadata.GroupMetadata, Is.EqualTo(MetadataGroup1.Metadata)); 90 | } 91 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Systems/MetadataSystem1.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.MetadataTests.Systems; 5 | 6 | [UpdateInGroup(typeof(SimulationSystemGroup))] 7 | [Custom1("TestValue1")] 8 | [Custom2(100, 200.5f)] 9 | [Custom2(1, 2f)] 10 | public partial class MetadataSystem1 : PlayerLoopSystem 11 | { 12 | internal MetadataSystem1(TestWorld world) : base(world) 13 | { 14 | } 15 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/MetadataTests/Systems/MetadataSystem2.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.Tests.TestSetup1; 2 | 3 | namespace Arch.SystemGroups.Tests.MetadataTests.Systems; 4 | 5 | [UpdateInGroup(typeof(Groups.MetadataGroup1))] 6 | [Custom1("TestValue2")] 7 | [Custom2(0, 0f)] 8 | [Custom2(1, 1f)] 9 | [Custom2(2, 2f)] 10 | [Custom2(3, 3f)] 11 | public partial class MetadataSystem2 : PlayerLoopSystem 12 | { 13 | internal MetadataSystem2(TestWorld world) : base(world) 14 | { 15 | } 16 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/NestedGroups/NestedGroups.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.NestedGroups; 5 | 6 | [UpdateInGroup(typeof(RootGroup))] 7 | public partial class NestedGroup1 8 | { 9 | 10 | } 11 | 12 | [UpdateInGroup(typeof(NestedGroup1))] 13 | public partial class NestedGroup2 14 | { 15 | 16 | } 17 | 18 | [UpdateInGroup(typeof(NestedGroup2))] 19 | public partial class SystemInNestedGroup : BaseSystem 20 | { 21 | public SystemInNestedGroup(TestWorld world) : base(world) 22 | { 23 | } 24 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/NestedGroups/NestedGroupsTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | using NSubstitute; 4 | 5 | namespace Arch.SystemGroups.Tests.NestedGroups; 6 | 7 | public class NestedGroupsTests 8 | { 9 | private ArchSystemsWorldBuilder _worldBuilder; 10 | private IPlayerLoop? _loopHelper; 11 | 12 | private SystemInNestedGroup _system; 13 | 14 | [SetUp] 15 | public void SetUp() 16 | { 17 | _worldBuilder = 18 | new ArchSystemsWorldBuilder(new TestWorld(), 19 | _loopHelper = Substitute.For()); 20 | 21 | _system = SystemInNestedGroup.InjectToWorld(ref _worldBuilder); 22 | } 23 | 24 | [Test] 25 | public void BuildsHierarchy() 26 | { 27 | var world = _worldBuilder.Finish(); 28 | 29 | var simGroup = world.SystemGroups.OfType().First(); 30 | var firstLevel = simGroup.Nodes; 31 | 32 | AssertHelpers.AssertNodesEquivalency(firstLevel, typeof(RootGroup)); 33 | 34 | var secondLevel = firstLevel.Find().Nodes; 35 | AssertHelpers.AssertNodesEquivalency(secondLevel, typeof(NestedGroup1)); 36 | 37 | var thirdLevel = secondLevel.Find().Nodes; 38 | AssertHelpers.AssertNodesEquivalency(thirdLevel, typeof(NestedGroup2)); 39 | 40 | var forthLevel = thirdLevel.Find().Nodes; 41 | AssertHelpers.AssertNodesEquivalency(forthLevel, typeof(SystemInNestedGroup)); 42 | } 43 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/NestedGroups/RootGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.NestedGroups; 4 | 5 | [UpdateInGroup(typeof(SimulationSystemGroup))] 6 | public partial class RootGroup 7 | { 8 | 9 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/PlayerLoopTests/OrderedSystemGroupAggregateTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.PlayerLoopTests; 4 | 5 | public class OrderedSystemGroupAggregateTests 6 | { 7 | [Test] 8 | public void DebouncesEqualValues() 9 | { 10 | var aggregate = new OrderedSystemGroupAggregate(Comparer.Default, true); 11 | 12 | aggregate.Add(100, new PresentationSystemGroup(new List>(), null, null)); 13 | Assert.DoesNotThrow(() => aggregate.Add(100, new PresentationSystemGroup(new List>(), null, null))); 14 | } 15 | 16 | [Test] 17 | public void ThrowsIfNotDebounced() 18 | { 19 | var aggregate = new OrderedSystemGroupAggregate(Comparer.Default, false); 20 | 21 | aggregate.Add(100, new PresentationSystemGroup(new List>(), null, null)); 22 | Assert.Throws(() => aggregate.Add(100, new PresentationSystemGroup(new List>(), null, null))); 23 | } 24 | 25 | [Test] 26 | public void SortsSystemGroups() 27 | { 28 | var aggregate = new OrderedSystemGroupAggregate(Comparer.Default); 29 | 30 | var group1 = new PresentationSystemGroup(new List>(), null, null); 31 | var group2 = new PresentationSystemGroup(new List>(), null, null); 32 | var group3 = new PresentationSystemGroup(new List>(), null, null); 33 | 34 | aggregate.Add(100, group1); 35 | aggregate.Add(50, group2); 36 | aggregate.Add(200, group3); 37 | 38 | Assert.That(aggregate.Count, Is.EqualTo(3)); 39 | CollectionAssert.AreEqual(new [] {group2, group1, group3}, aggregate.Values); 40 | } 41 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/RedundantDependencies/RedundantDependenciesSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | 5 | namespace Arch.SystemGroups.Tests.RedundantDependencies; 6 | 7 | [UpdateInGroup(typeof(SimulationSystemGroup))] 8 | [UpdateBefore(typeof(RDSystem2))] 9 | [UpdateBefore(typeof(RDSystem4))] 10 | public partial class RDSystem1 : BaseSystem 11 | { 12 | public RDSystem1(TestWorld world) : base(world) 13 | { 14 | } 15 | } 16 | 17 | [UpdateInGroup(typeof(SimulationSystemGroup))] 18 | [UpdateBefore(typeof(RDSystem3))] 19 | public partial class RDSystem2 : BaseSystem 20 | { 21 | public RDSystem2(TestWorld world) : base(world) 22 | { 23 | } 24 | } 25 | 26 | [UpdateInGroup(typeof(SimulationSystemGroup))] 27 | [UpdateAfter(typeof(RDSystem1))] 28 | [UpdateBefore(typeof(RDSystem4))] 29 | [UpdateAfter(typeof(RDSystem2))] 30 | public partial class RDSystem3 : BaseSystem 31 | { 32 | public RDSystem3(TestWorld world) : base(world) 33 | { 34 | } 35 | } 36 | 37 | [UpdateAfter(typeof(RDSystem2))] 38 | [UpdateInGroup(typeof(SimulationSystemGroup))] 39 | public partial class RDSystem4 : BaseSystem 40 | { 41 | public RDSystem4(TestWorld world) : base(world) 42 | { 43 | } 44 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/RedundantDependencies/RedundantDependenciesTests.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1; 4 | using Arch.SystemGroups.Tests.TestSetup1.Groups; 5 | using Arch.SystemGroups.Tests.TestSetup1.Systems; 6 | using NSubstitute; 7 | 8 | namespace Arch.SystemGroups.Tests.RedundantDependencies; 9 | 10 | public class RedundantDependenciesTests 11 | { 12 | private ArchSystemsWorldBuilder _worldBuilder; 13 | private IPlayerLoop? _playerLoop; 14 | 15 | private RDSystem1 _rdSystem1; 16 | private RDSystem2 _rdSystem2; 17 | private RDSystem3 _rdSystem3; 18 | private RDSystem4 _rdSystem4; 19 | 20 | [SetUp] 21 | public void SetUp() 22 | { 23 | _worldBuilder = 24 | new ArchSystemsWorldBuilder(new TestWorld(), 25 | _playerLoop = Substitute.For()); 26 | 27 | _rdSystem3 = RDSystem3.InjectToWorld(ref _worldBuilder); 28 | _rdSystem1 = RDSystem1.InjectToWorld(ref _worldBuilder); 29 | _rdSystem4 = RDSystem4.InjectToWorld(ref _worldBuilder); 30 | _rdSystem2 = RDSystem2.InjectToWorld(ref _worldBuilder); 31 | } 32 | 33 | [Test] 34 | public void Sorts() 35 | { 36 | var world = _worldBuilder.Finish(); 37 | 38 | var systemGroup = world.SystemGroups.OfType().First(); 39 | var nodes = systemGroup.Nodes; 40 | 41 | AssertHelpers.AssertNodesEquality(nodes, typeof(RDSystem1), typeof(RDSystem2), typeof(RDSystem3), typeof(RDSystem4)); 42 | } 43 | 44 | [Test] 45 | [TestCase(typeof(RDSystem1), new[] { typeof(RDSystem2), typeof(RDSystem4) }, new Type[0])] 46 | [TestCase(typeof(RDSystem2), new[] { typeof(RDSystem3) }, new Type[0])] 47 | [TestCase(typeof(RDSystem3), new[] { typeof(RDSystem4) }, new[] { typeof(RDSystem1), typeof(RDSystem2) })] 48 | [TestCase(typeof(RDSystem4), new Type[0], new[] { typeof(RDSystem2) })] 49 | public void RespectsAllEdges(Type system, Type[] updateBefore, Type[] updateAfter) 50 | { 51 | var map = new Dictionary>(); 52 | 53 | // find method by reflection 54 | var addEdgesMethod = system.GetMethod("AddEdges", BindingFlags.NonPublic | BindingFlags.Static); 55 | addEdgesMethod!.Invoke(null, new object[] { map }); 56 | 57 | // Check update before 58 | if (updateBefore.Length > 0) 59 | { 60 | Assert.That(map.TryGetValue(system, out var before), Is.True); 61 | Assert.That(before, Is.EquivalentTo(updateBefore)); 62 | } 63 | 64 | // Check update after 65 | if (updateAfter.Length > 0) 66 | { 67 | foreach (var after in updateAfter) 68 | { 69 | Assert.That(map.TryGetValue(after, out var afterList), Is.True); 70 | Assert.That(afterList, Contains.Item(system)); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/CustomClass1.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.TestSetup1; 2 | 3 | public class CustomClass1 4 | { 5 | public int Value { get; set; } 6 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/Groups/CustomGroup1.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.TestSetup1.Groups; 4 | 5 | [UpdateInGroup(typeof(InitializationSystemGroup))] 6 | public partial class CustomGroup1 7 | { 8 | 9 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/Systems/CustomSystem1.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | using Arch.SystemGroups.Tests.TestSetup1.Groups; 4 | 5 | namespace Arch.SystemGroups.Tests.TestSetup1.Systems; 6 | 7 | [UpdateInGroup(typeof(InitializationSystemGroup))] 8 | [UpdateBefore(typeof(CustomGroup1))] 9 | public partial class CustomSystem1 : BaseSystem 10 | { 11 | public bool IsInitialized { get; private set; } 12 | public bool IsDisposed { get; private set; } 13 | 14 | public CustomSystem1(TestWorld world) : base(world) 15 | { 16 | } 17 | 18 | public override void Initialize() 19 | { 20 | IsInitialized = true; 21 | } 22 | 23 | public override void Dispose() 24 | { 25 | IsDisposed = true; 26 | } 27 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/Systems/CustomSystem1InCustomGroup1.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1.Groups; 3 | 4 | namespace Arch.SystemGroups.Tests.TestSetup1.Systems; 5 | 6 | [UpdateInGroup(typeof(CustomGroup1))] 7 | public partial class CustomSystem1InCustomGroup1 : BaseSystem 8 | { 9 | public bool IsInitialized { get; private set; } 10 | public bool IsDisposed { get; private set; } 11 | 12 | public CustomSystem1InCustomGroup1(TestWorld world, double arg1, Action f) : base(world) 13 | { 14 | } 15 | 16 | public override void Initialize() 17 | { 18 | IsInitialized = true; 19 | } 20 | 21 | public override void Dispose() 22 | { 23 | IsDisposed = true; 24 | } 25 | } 26 | 27 | [UpdateInGroup(typeof(CustomGroup1))] 28 | public partial class CustomSystem2InCustomGroup1 : BaseSystem 29 | { 30 | public bool IsInitialized { get; private set; } 31 | public bool IsDisposed { get; private set; } 32 | 33 | public CustomSystem2InCustomGroup1(TestWorld world, double customName = 0.2, Action? f = null) : base(world) 34 | { 35 | } 36 | 37 | public override void Initialize() 38 | { 39 | IsInitialized = true; 40 | } 41 | 42 | public override void Dispose() 43 | { 44 | IsDisposed = true; 45 | } 46 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/Systems/CustomSystemWithParameters1.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | 4 | namespace Arch.SystemGroups.Tests.TestSetup1.Systems; 5 | 6 | [UpdateInGroup(typeof(InitializationSystemGroup))] 7 | public partial class CustomSystemWithParameters1 : BaseSystem 8 | { 9 | private readonly string _param1; 10 | private readonly int _param2; 11 | 12 | public bool IsInitialized { get; private set; } 13 | public bool IsDisposed { get; private set; } 14 | 15 | public CustomSystemWithParameters1(TestWorld world, string param1, int param2) : base(world) 16 | { 17 | _param1 = param1; 18 | _param2 = param2; 19 | } 20 | 21 | public override void Initialize() 22 | { 23 | IsInitialized = true; 24 | } 25 | 26 | public override void Dispose() 27 | { 28 | IsDisposed = true; 29 | } 30 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/Systems/CustomSystemWithParameters2.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.DefaultSystemGroups; 3 | 4 | namespace Arch.SystemGroups.Tests.TestSetup1.Systems; 5 | 6 | [UpdateInGroup(typeof(InitializationSystemGroup))] 7 | [UpdateAfter(typeof(CustomSystemWithParameters1))] 8 | [UpdateBefore(typeof(CustomSystem1))] 9 | public partial class CustomSystemWithParameters2 : BaseSystem 10 | { 11 | public readonly CustomClass1 CustomClass1; 12 | 13 | public bool IsInitialized { get; private set; } 14 | public bool IsDisposed { get; private set; } 15 | 16 | public CustomSystemWithParameters2(TestWorld world, CustomClass1 customClass1) : base(world) 17 | { 18 | CustomClass1 = customClass1; 19 | } 20 | 21 | public override void Initialize() 22 | { 23 | IsInitialized = true; 24 | } 25 | 26 | public override void Dispose() 27 | { 28 | IsDisposed = true; 29 | } 30 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/Systems/DisconnectedSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | 3 | namespace Arch.SystemGroups.Tests.TestSetup1.Systems; 4 | 5 | /// 6 | /// No code generation should be done for this system. 7 | /// 8 | public class DisconnectedSystem : BaseSystem 9 | { 10 | public DisconnectedSystem(TestWorld world) : base(world) 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/TestSetup1Tests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup1.Groups; 3 | using Arch.SystemGroups.Tests.TestSetup1.Systems; 4 | using NSubstitute; 5 | 6 | namespace Arch.SystemGroups.Tests.TestSetup1; 7 | 8 | public class TestSetup1Tests 9 | { 10 | private CustomSystem1 _customSystem1; 11 | private CustomSystem1InCustomGroup1 _customSystem1InCustomGroup1; 12 | private CustomSystemWithParameters1 _customSystemWithParameters1; 13 | private CustomSystemWithParameters2 _customSystemWithParameters2; 14 | private IPlayerLoop _playerLoop; 15 | private ArchSystemsWorldBuilder _worldBuilder; 16 | 17 | [SetUp] 18 | public void SetUp() 19 | { 20 | _worldBuilder = 21 | new ArchSystemsWorldBuilder(new TestWorld(), 22 | _playerLoop = Substitute.For()); 23 | 24 | _customSystem1 = CustomSystem1.InjectToWorld(ref _worldBuilder); 25 | _customSystemWithParameters1 = CustomSystemWithParameters1.InjectToWorld(ref _worldBuilder, "test", 1); 26 | _customSystemWithParameters2 = 27 | CustomSystemWithParameters2.InjectToWorld(ref _worldBuilder, new CustomClass1 { Value = 200 }); 28 | _customSystem1InCustomGroup1 = CustomSystem1InCustomGroup1.InjectToWorld(ref _worldBuilder, 1.0, (f, i) => { }); 29 | } 30 | 31 | [Test] 32 | public void Sorts() 33 | { 34 | var world = _worldBuilder.Finish(); 35 | 36 | AssertHelpers 37 | .AssertOrderOfSystemIsLessThenOtherSystem(world); 38 | AssertHelpers 39 | .AssertOrderOfSystemIsLessThenOtherSystem(world); 41 | AssertHelpers 42 | .AssertOrderOfSystemIsLessThenOtherSystem(world); 44 | } 45 | 46 | [Test] 47 | public void InitializesSystems() 48 | { 49 | var world = _worldBuilder.Finish(); 50 | world.Initialize(); 51 | 52 | Assert.That(_customSystem1.IsInitialized, Is.True); 53 | Assert.That(_customSystemWithParameters1.IsInitialized, Is.True); 54 | Assert.That(_customSystemWithParameters2.IsInitialized, Is.True); 55 | Assert.That(_customSystem1InCustomGroup1.IsInitialized, Is.True); 56 | } 57 | 58 | [Test] 59 | public void DisposesSystems() 60 | { 61 | var world = _worldBuilder.Finish(); 62 | world.Dispose(); 63 | 64 | Assert.That(_customSystem1.IsDisposed, Is.True); 65 | Assert.That(_customSystemWithParameters1.IsDisposed, Is.True); 66 | Assert.That(_customSystemWithParameters2.IsDisposed, Is.True); 67 | Assert.That(_customSystem1InCustomGroup1.IsDisposed, Is.True); 68 | } 69 | 70 | [Test] 71 | public void BuildsHierarchy() 72 | { 73 | var world = _worldBuilder.Finish(); 74 | 75 | // In the initialization group there should be 3 systems and 1 group 76 | var initGroup = world.SystemGroups.OfType().First(); 77 | var firstLevelSystems = initGroup.Nodes; 78 | 79 | AssertHelpers.AssertNodesEquivalency(firstLevelSystems, 80 | typeof(CustomSystemWithParameters1), 81 | typeof(CustomSystemWithParameters2), 82 | typeof(CustomSystem1), 83 | typeof(CustomGroup1)); 84 | 85 | var customGroup1 = firstLevelSystems.Find(); 86 | AssertHelpers.AssertNodesEquivalency(customGroup1.Nodes, typeof(CustomSystem1InCustomGroup1)); 87 | } 88 | 89 | [Test] 90 | public void UnusedSystemGroupsAreEmpty() 91 | { 92 | var world = _worldBuilder.Finish(); 93 | 94 | void AssertIsEmpty() where T : SystemGroup 95 | { 96 | var group = world.SystemGroups.OfType().First(); 97 | Assert.That(group.Nodes, Is.Null); 98 | } 99 | 100 | AssertIsEmpty(); 101 | AssertIsEmpty(); 102 | AssertIsEmpty(); 103 | AssertIsEmpty(); 104 | AssertIsEmpty(); 105 | } 106 | 107 | [Test] 108 | public void ThrowsExceptionIfRepetitiveSystemAdded() 109 | { 110 | Assert.Throws(() => 111 | { 112 | CustomSystemWithParameters2.InjectToWorld(ref _worldBuilder, new CustomClass1 { Value = 400 }); 113 | }); 114 | } 115 | 116 | [Test] 117 | public void Disposes() 118 | { 119 | // can't test as Pools are not mockable 120 | } 121 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup1/TestWorld.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.TestSetup1; 2 | 3 | public class TestWorld 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup2/Groups/GroupsA.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.TestSetup2.Groups; 4 | 5 | [UpdateInGroup(typeof(PhysicsSystemGroup))] 6 | [UpdateBefore(typeof(GroupAB))] 7 | public partial class GroupAA 8 | { 9 | 10 | } 11 | 12 | [UpdateInGroup(typeof(PhysicsSystemGroup))] 13 | [UpdateAfter(typeof(GroupAA))] 14 | public partial class GroupAB 15 | { 16 | 17 | } 18 | 19 | [UpdateInGroup(typeof(PhysicsSystemGroup))] 20 | [UpdateAfter(typeof(GroupAB))] 21 | public partial class GroupAC 22 | { 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup2/Groups/GroupsB.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.TestSetup2.Groups; 4 | 5 | [UpdateInGroup(typeof(PostRenderingSystemGroup))] 6 | public partial class GroupBA 7 | { 8 | } 9 | 10 | [UpdateInGroup(typeof(PostRenderingSystemGroup))] 11 | [UpdateAfter(typeof(GroupBA))] 12 | public partial class GroupBB 13 | { 14 | } 15 | 16 | [UpdateInGroup(typeof(GroupBA))] 17 | // This dependency should not be taken into consideration even as 18 | // this group does not belong to the GroupBB hierarchy. 19 | // [UpdateAfter(typeof(GroupBB))] no tolerance 20 | public partial class GroupBAA 21 | { 22 | } 23 | 24 | [UpdateInGroup(typeof(GroupBA))] 25 | [UpdateAfter(typeof(GroupBAA))] 26 | // This dependency should not be taken into consideration even as 27 | // this group does not belong to the A hierarchy. 28 | // [UpdateBefore(typeof(GroupAA))] no tolerance 29 | public partial class GroupBAB 30 | { 31 | } 32 | 33 | [UpdateInGroup(typeof(PostRenderingSystemGroup))] 34 | // Will be never created as it is empty 35 | public partial class GroupBC 36 | { 37 | 38 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup2/Systems/SystemsA.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup2.Groups; 3 | 4 | namespace Arch.SystemGroups.Tests.TestSetup2.Systems; 5 | 6 | [UpdateInGroup(typeof(GroupAA))] 7 | [UpdateBefore(typeof(SystemCGroupAA))] 8 | public partial class SystemAGroupAA : BaseSystem 9 | { 10 | public SystemAGroupAA(TestWorld2 world) : base(world) 11 | { 12 | } 13 | } 14 | 15 | [UpdateBefore(typeof(SystemCGroupAA))] 16 | [UpdateAfter(typeof(SystemAGroupAA))] 17 | [UpdateInGroup(typeof(GroupAA))] 18 | public partial class SystemBGroupAA : BaseSystem 19 | { 20 | public SystemBGroupAA(TestWorld2 world) : base(world) 21 | { 22 | } 23 | } 24 | 25 | [UpdateInGroup(typeof(GroupAA))] 26 | public partial class SystemCGroupAA : BaseSystem 27 | { 28 | public SystemCGroupAA(TestWorld2 world) : base(world) 29 | { 30 | } 31 | } 32 | 33 | // Group AB 34 | 35 | [UpdateInGroup(typeof(GroupAB))] 36 | // [UpdateBefore(typeof(SystemCGroupAA))] // no tolerance 37 | [UpdateBefore(typeof(SystemCGroupAB))] 38 | public partial class SystemAGroupAB : BaseSystem 39 | { 40 | public SystemAGroupAB(TestWorld2 world) : base(world) 41 | { 42 | } 43 | } 44 | 45 | [UpdateBefore(typeof(SystemCGroupAB))] 46 | [UpdateAfter(typeof(SystemAGroupAB))] 47 | [UpdateInGroup(typeof(GroupAB))] 48 | public partial class SystemBGroupAB : BaseSystem 49 | { 50 | public SystemBGroupAB(TestWorld2 world) : base(world) 51 | { 52 | } 53 | } 54 | 55 | [UpdateInGroup(typeof(GroupAB))] 56 | public partial class SystemCGroupAB : BaseSystem 57 | { 58 | public SystemCGroupAB(TestWorld2 world) : base(world) 59 | { 60 | } 61 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup2/Systems/SystemsB.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup2.Groups; 3 | 4 | namespace Arch.SystemGroups.Tests.TestSetup2.Systems; 5 | 6 | [UpdateInGroup(typeof(GroupBA))] 7 | // Redundant attributes should not matter 8 | [UpdateBefore(typeof(SystemCGroupBA))] 9 | [UpdateBefore(typeof(SystemBGroupBA))] 10 | [UpdateBefore(typeof(SystemDGroupBA))] 11 | public partial class SystemAGroupBA : BaseSystem 12 | { 13 | public SystemAGroupBA(TestWorld2 world) : base(world) 14 | { 15 | } 16 | } 17 | 18 | [UpdateInGroup(typeof(GroupBA))] 19 | [UpdateAfter(typeof(SystemAGroupBA))] 20 | public partial class SystemBGroupBA : BaseSystem 21 | { 22 | public SystemBGroupBA(TestWorld2 world) : base(world) 23 | { 24 | } 25 | } 26 | 27 | [UpdateInGroup(typeof(GroupBA))] 28 | [UpdateAfter(typeof(SystemBGroupBA))] 29 | [UpdateAfter(typeof(SystemAGroupBA))] 30 | public partial class SystemCGroupBA : BaseSystem 31 | { 32 | public SystemCGroupBA(TestWorld2 world) : base(world) 33 | { 34 | } 35 | } 36 | 37 | [UpdateInGroup(typeof(GroupBA))] 38 | [UpdateAfter(typeof(SystemCGroupBA))] 39 | [UpdateAfter(typeof(SystemBGroupBA))] 40 | [UpdateAfter(typeof(SystemAGroupBA))] 41 | [UpdateAfter(typeof(SystemDGroupBA))] // <-- will be ignored by the generator 42 | public partial class SystemDGroupBA : BaseSystem 43 | { 44 | public SystemDGroupBA(TestWorld2 world) : base(world) 45 | { 46 | } 47 | } 48 | 49 | [UpdateInGroup(typeof(GroupBB))] 50 | [UpdateBefore(typeof(SystemBGroupBB))] 51 | // The following edges will be ignored as belong to a different group 52 | // [UpdateAfter(typeof(SystemCGroupBA))] no tolerance 53 | // [UpdateAfter(typeof(SystemBGroupBA))] 54 | // [UpdateAfter(typeof(SystemAGroupBA))] 55 | // [UpdateAfter(typeof(SystemDGroupBA))] 56 | public partial class SystemAGroupBB : BaseSystem 57 | { 58 | public SystemAGroupBB(TestWorld2 world) : base(world) 59 | { 60 | } 61 | } 62 | 63 | [UpdateInGroup(typeof(GroupBB))] 64 | public partial class SystemBGroupBB : BaseSystem 65 | { 66 | public SystemBGroupBB(TestWorld2 world) : base(world) 67 | { 68 | } 69 | } 70 | 71 | [UpdateInGroup(typeof(GroupBAA))] 72 | [UpdateBefore(typeof(SystemBGroupBAA))] 73 | public partial class SystemAGroupBAA : BaseSystem 74 | { 75 | public SystemAGroupBAA(TestWorld2 world) : base(world) 76 | { 77 | } 78 | } 79 | 80 | [UpdateInGroup(typeof(GroupBAA))] 81 | [UpdateBefore(typeof(SystemCGroupBAA))] 82 | public partial class SystemBGroupBAA : BaseSystem 83 | { 84 | public SystemBGroupBAA(TestWorld2 world) : base(world) 85 | { 86 | } 87 | } 88 | 89 | [UpdateInGroup(typeof(GroupBAA))] 90 | public partial class SystemCGroupBAA : BaseSystem 91 | { 92 | public SystemCGroupBAA(TestWorld2 world) : base(world) 93 | { 94 | } 95 | } 96 | 97 | [UpdateInGroup(typeof(GroupBAB))] 98 | public partial class SystemAGroupBAB : BaseSystem 99 | { 100 | public SystemAGroupBAB(TestWorld2 world) : base(world) 101 | { 102 | } 103 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup2/TestSetup2Tests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup2.Groups; 3 | using Arch.SystemGroups.Tests.TestSetup2.Systems; 4 | using NSubstitute; 5 | 6 | namespace Arch.SystemGroups.Tests.TestSetup2; 7 | 8 | public class TestSetup2Tests 9 | { 10 | private ArchSystemsWorldBuilder _worldBuilder; 11 | private IPlayerLoop? _loopHelper; 12 | 13 | [SetUp] 14 | public void Setup() 15 | { 16 | _worldBuilder = new ArchSystemsWorldBuilder(new TestWorld2(), _loopHelper = Substitute.For()); 17 | 18 | /*_worldBuilder 19 | .AddSystemCGroupAA() 20 | .AddSystemCGroupAB() 21 | .AddSystemAGroupAA() 22 | .AddSystemAGroupAB() 23 | .AddSystemBGroupAA() 24 | .AddSystemBGroupAB() 25 | 26 | .AddSystemDGroupBA() 27 | .AddSystemCGroupBA() 28 | .AddSystemCGroupBAA() 29 | .AddSystemBGroupBA() 30 | .AddSystemBGroupBB() 31 | .AddSystemBGroupBAA() 32 | .AddSystemAGroupBA() 33 | .AddSystemAGroupBB() 34 | .AddSystemAGroupBAA() 35 | .AddSystemAGroupBAB(); 36 | is equivalent to AddAllSystems() 37 | */ 38 | 39 | _worldBuilder.AddAllSystems(); 40 | } 41 | 42 | [Test] 43 | public void SortsPhysicsSystemGroup() 44 | { 45 | var world = _worldBuilder.Finish(); 46 | 47 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(world); 48 | 49 | Assert.Throws(() => 50 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(world)); 51 | 52 | var physicsSystemGroup = world.SystemGroups.OfType().First(); 53 | 54 | // group AA 55 | var aaSystems = physicsSystemGroup.Nodes.Find().Nodes; 56 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(aaSystems); 57 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(aaSystems); 58 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(aaSystems); 59 | 60 | // group AB 61 | var abSystems = physicsSystemGroup.Nodes.Find().Nodes; 62 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(abSystems); 63 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(abSystems); 64 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(abSystems); 65 | 66 | } 67 | 68 | [Test] 69 | public void SortsPostRenderingSystemGroup() 70 | { 71 | var world = _worldBuilder.Finish(); 72 | 73 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(world); 74 | Assert.Throws(() => 75 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(world)); 76 | 77 | var firstLevelGroups = world.SystemGroups.OfType().First().Nodes; 78 | 79 | // group BA 80 | var ba = firstLevelGroups.Find().Nodes; 81 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 82 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 83 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 84 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 85 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 86 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 87 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 88 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(ba); 89 | 90 | // group BB 91 | var bb = firstLevelGroups.Find().Nodes; 92 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(bb); 93 | 94 | // group BAA 95 | var baa = ba.Find().Nodes; 96 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(baa); 97 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(baa); 98 | AssertHelpers.AssertOrderOfSystemIsLessThenOtherSystem(baa); 99 | } 100 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/TestSetup2/TestWorld2.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.TestSetup2; 2 | 3 | public class TestWorld2 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ParametrisedThrottleGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 4 | 5 | [UpdateInGroup(typeof(SimulationSystemGroup))] 6 | public partial class ParametrisedThrottleGroup : ThrottleGroupBase 7 | { 8 | public ParametrisedThrottleGroup(int framesToSkip) : base(framesToSkip) 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ParametrisedThrottleSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 5 | 6 | [UpdateInGroup(typeof(ParametrisedThrottleGroup))] 7 | public partial class ParametrisedThrottleSystem : BaseSystem 8 | { 9 | public ParametrisedThrottleSystem(TestWorld world) : base(world) 10 | { 11 | } 12 | 13 | public int UpdateCount { get; private set; } 14 | public int BeforeUpdateCount { get; private set; } 15 | public int AfterUpdateCount { get; private set; } 16 | 17 | public override void BeforeUpdate(in float t) 18 | { 19 | BeforeUpdateCount++; 20 | } 21 | 22 | public override void AfterUpdate(in float t) 23 | { 24 | AfterUpdateCount++; 25 | } 26 | 27 | public override void Update(in float t) 28 | { 29 | UpdateCount++; 30 | } 31 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ThrottleGroupBase.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 2 | 3 | /// 4 | /// Skips every other update 5 | /// 6 | public abstract class ThrottleGroupBase : CustomGroupBase 7 | { 8 | private readonly int _framesToSkip; 9 | private int _counter; 10 | 11 | private bool _open; 12 | 13 | protected ThrottleGroupBase(int framesToSkip) 14 | { 15 | _framesToSkip = framesToSkip; 16 | _counter = framesToSkip; 17 | } 18 | 19 | public override void Dispose() 20 | { 21 | DisposeInternal(); 22 | } 23 | 24 | public override void Initialize() 25 | { 26 | InitializeInternal(); 27 | } 28 | 29 | public override void BeforeUpdate(in float t, bool throttle) 30 | { 31 | // Before Update is always called first in the same frame 32 | _open = _counter >= _framesToSkip; 33 | _counter++; 34 | if (_open) 35 | { 36 | BeforeUpdateInternal(in t, throttle); 37 | _counter = 0; 38 | } 39 | } 40 | 41 | public override void Update(in float t, bool throttle) 42 | { 43 | if (_open) 44 | UpdateInternal(in t, throttle); 45 | } 46 | 47 | public override void AfterUpdate(in float t, bool throttle) 48 | { 49 | if (_open) 50 | AfterUpdateInternal(in t, throttle); 51 | } 52 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ThrottleGroupTests.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | using NSubstitute; 4 | 5 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 6 | 7 | public class ThrottleGroupTests 8 | { 9 | private IPlayerLoop _playerLoop; 10 | private ThrottlePostRenderingSystem _postRenderingSystem; 11 | private ThrottleSimulationSystem _simulationSystem; 12 | 13 | private ArchSystemsWorldBuilder _worldBuilder; 14 | 15 | [SetUp] 16 | public void SetUp() 17 | { 18 | _worldBuilder = 19 | new ArchSystemsWorldBuilder(new TestWorld(), 20 | _playerLoop = Substitute.For()); 21 | 22 | _simulationSystem = ThrottleSimulationSystem.InjectToWorld(ref _worldBuilder); 23 | _postRenderingSystem = ThrottlePostRenderingSystem.InjectToWorld(ref _worldBuilder); 24 | } 25 | 26 | [TearDown] 27 | public void TearDown() 28 | { 29 | PlayerLoopHelper.AggregatesCache.Clear(); 30 | } 31 | 32 | [Test] 33 | public void BuildsHierarchy() 34 | { 35 | var world = _worldBuilder.Finish(); 36 | 37 | AssertHelpers.AssertNodesEquivalency(world.SystemGroups.OfType().First().Nodes, typeof(ThrottleSimulationGroup)); 38 | AssertHelpers.AssertNodesEquivalency(world.SystemGroups.OfType().First().Nodes, typeof(ThrottlePostRenderingGroup)); 39 | } 40 | 41 | [Test] 42 | public void Throttles() 43 | { 44 | // Can't test it because Time.deltaTime can't be used outside of Unity: System.Security.SecurityException : ECall methods must be packaged into a system module. 45 | var systemGroups = new List(); 46 | 47 | _playerLoop.When(p => p.AddAggregate(typeof(SimulationSystemGroup), Arg.Any>())) 48 | .Do(c => systemGroups.Add(c.Arg>())); 49 | 50 | _playerLoop.When(p => p.AddAggregate(typeof(PostRenderingSystemGroup), Arg.Any>())) 51 | .Do(c => systemGroups.Add(c.Arg>())); 52 | 53 | var world = _worldBuilder.Finish(); 54 | world.Initialize(); 55 | 56 | for (var i = 0; i < 10; i++) 57 | { 58 | foreach (var systemGroup in systemGroups) 59 | { 60 | systemGroup.TriggerUpdate(); 61 | } 62 | } 63 | 64 | Assert.That(_simulationSystem.UpdateCount, Is.EqualTo(5)); 65 | Assert.That(_postRenderingSystem.UpdateCount, Is.EqualTo(5)); 66 | Assert.That(_simulationSystem.BeforeUpdateCount, Is.EqualTo(5)); 67 | Assert.That(_postRenderingSystem.BeforeUpdateCount, Is.EqualTo(5)); 68 | Assert.That(_simulationSystem.AfterUpdateCount, Is.EqualTo(5)); 69 | Assert.That(_postRenderingSystem.AfterUpdateCount, Is.EqualTo(5)); 70 | } 71 | 72 | [Test] 73 | public void ThrowsIfNotInjectedManually() 74 | { 75 | Assert.Throws(() => ParametrisedThrottleSystem.InjectToWorld(ref _worldBuilder)); 76 | _worldBuilder.Finish(); 77 | } 78 | 79 | [Test] 80 | public void ThrottlesParametrizedSystem([Values(1, 2, 5, 10)] int framesToSkip) 81 | { 82 | const int framesToRun = 100; 83 | 84 | _worldBuilder.InjectCustomGroup(new ParametrisedThrottleGroup(framesToSkip)); 85 | 86 | var system = ParametrisedThrottleSystem.InjectToWorld(ref _worldBuilder); 87 | 88 | var systemGroups = new List(); 89 | 90 | _playerLoop.When(p => p.AddAggregate(typeof(SimulationSystemGroup), Arg.Any>())) 91 | .Do(c => systemGroups.Add(c.Arg>())); 92 | 93 | var world = _worldBuilder.Finish(); 94 | world.Initialize(); 95 | 96 | for (var i = 0; i < framesToRun; i++) 97 | { 98 | foreach (var systemGroup in systemGroups) 99 | { 100 | systemGroup.TriggerUpdate(); 101 | } 102 | } 103 | 104 | var expectedUpdateCount = 1 + (framesToRun - 1) / (framesToSkip + 1); 105 | 106 | Assert.That(system.UpdateCount, Is.EqualTo(expectedUpdateCount)); 107 | Assert.That(system.BeforeUpdateCount, Is.EqualTo(expectedUpdateCount)); 108 | Assert.That(system.AfterUpdateCount, Is.EqualTo(expectedUpdateCount)); 109 | } 110 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ThrottlePostRenderingGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 4 | 5 | [UpdateInGroup(typeof(PostRenderingSystemGroup))] 6 | public partial class ThrottlePostRenderingGroup : ThrottleGroupBase 7 | { 8 | public ThrottlePostRenderingGroup() : base(1) 9 | { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ThrottlePostRenderingSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 5 | 6 | [UpdateInGroup(typeof(ThrottlePostRenderingGroup))] 7 | public partial class ThrottlePostRenderingSystem : BaseSystem 8 | { 9 | public ThrottlePostRenderingSystem(TestWorld world) : base(world) 10 | { 11 | } 12 | 13 | public int UpdateCount { get; private set; } 14 | public int BeforeUpdateCount { get; private set; } 15 | public int AfterUpdateCount { get; private set; } 16 | 17 | public override void BeforeUpdate(in float t) 18 | { 19 | BeforeUpdateCount++; 20 | } 21 | 22 | public override void AfterUpdate(in float t) 23 | { 24 | AfterUpdateCount++; 25 | } 26 | 27 | public override void Update(in float t) 28 | { 29 | UpdateCount++; 30 | } 31 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ThrottleSimulationGroup.cs: -------------------------------------------------------------------------------- 1 | using Arch.SystemGroups.DefaultSystemGroups; 2 | 3 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 4 | 5 | [UpdateInGroup(typeof(SimulationSystemGroup))] 6 | public partial class ThrottleSimulationGroup : ThrottleGroupBase 7 | { 8 | public ThrottleSimulationGroup() : base(1) 9 | { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/ThrottleGroup/ThrottleSimulationSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Tests.TestSetup1; 3 | 4 | namespace Arch.SystemGroups.Tests.ThrottleGroup; 5 | 6 | [UpdateInGroup(typeof(ThrottleSimulationGroup))] 7 | public partial class ThrottleSimulationSystem : BaseSystem 8 | { 9 | public ThrottleSimulationSystem(TestWorld world) : base(world) 10 | { 11 | } 12 | 13 | public int UpdateCount { get; private set; } 14 | public int BeforeUpdateCount { get; private set; } 15 | public int AfterUpdateCount { get; private set; } 16 | 17 | public override void BeforeUpdate(in float t) 18 | { 19 | BeforeUpdateCount++; 20 | } 21 | 22 | public override void AfterUpdate(in float t) 23 | { 24 | AfterUpdateCount++; 25 | } 26 | 27 | public override void Update(in float t) 28 | { 29 | UpdateCount++; 30 | } 31 | } -------------------------------------------------------------------------------- /Arch.SystemGroups.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /Arch.SystemGroups.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33516.290 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.SystemGroups", "Arch.SystemGroups\Arch.SystemGroups.csproj", "{A6751876-4006-448E-AB91-B36F285AC27F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.SystemGroups.SourceGenerator", "Arch.SystemGroups.SourceGenerator\Arch.SystemGroups.SourceGenerator.csproj", "{F24534F8-CA79-48D2-ACB8-4011CD036B8E}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arch.SystemGroups.Tests", "Arch.SystemGroups.Tests\Arch.SystemGroups.Tests.csproj", "{A0472A57-50B4-4ED7-A87C-E7A5A20009B7}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{69A09E18-F753-431D-906E-9058C1711D27}" 13 | ProjectSection(SolutionItems) = preProject 14 | LICENSE = LICENSE 15 | README.md = README.md 16 | EndProjectSection 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arch.SystemGroups.NoUnity", "Arch.SystemGroups\Arch.SystemGroups.NoUnity.csproj", "{14029DAF-749A-4711-97E6-ECA880BEBDF3}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {A6751876-4006-448E-AB91-B36F285AC27F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {A6751876-4006-448E-AB91-B36F285AC27F}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {A6751876-4006-448E-AB91-B36F285AC27F}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {A6751876-4006-448E-AB91-B36F285AC27F}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {F24534F8-CA79-48D2-ACB8-4011CD036B8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {F24534F8-CA79-48D2-ACB8-4011CD036B8E}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {F24534F8-CA79-48D2-ACB8-4011CD036B8E}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {F24534F8-CA79-48D2-ACB8-4011CD036B8E}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {A0472A57-50B4-4ED7-A87C-E7A5A20009B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {A0472A57-50B4-4ED7-A87C-E7A5A20009B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {A0472A57-50B4-4ED7-A87C-E7A5A20009B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {A0472A57-50B4-4ED7-A87C-E7A5A20009B7}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {14029DAF-749A-4711-97E6-ECA880BEBDF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {14029DAF-749A-4711-97E6-ECA880BEBDF3}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {14029DAF-749A-4711-97E6-ECA880BEBDF3}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {14029DAF-749A-4711-97E6-ECA880BEBDF3}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {9EC41DFC-8BF9-4BDF-A4EE-09CB2843C247} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.NoUnity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | disable 6 | 10 7 | 8 | 9 | 10 | bin\Release\Arch.SystemGroups.xml 11 | TRACE;OUTSIDE_UNITY; 12 | 13 | 14 | 15 | TRACE;OUTSIDE_UNITY; 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ..\libs\UnityEngine.CoreModule.dll 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.NoUnity.csproj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6bff4b8bea0b602459c0c586d408b558 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Arch.SystemGroups", 3 | "references": [], 4 | "includePlatforms": [], 5 | "excludePlatforms": [], 6 | "allowUnsafeCode": false, 7 | "overrideReferences": false, 8 | "precompiledReferences": [], 9 | "autoReferenced": true, 10 | "defineConstraints": [], 11 | "versionDefines": [], 12 | "noEngineReferences": false 13 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 275e22790c04e9b47a5085d7b0c4432a 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | disable 6 | 10 7 | 8 | 9 | 10 | bin\Release\Arch.SystemGroups.xml 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ..\libs\UnityEngine.CoreModule.dll 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.csproj.DotSettings.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3cab9c4684aed5d46a4935caa1cfb9fa 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Arch.SystemGroups.csproj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5268b02a4d923784ebd6f8175ed05aac 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Arch.SystemGroups/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Arch.SystemGroups.Tests")] -------------------------------------------------------------------------------- /Arch.SystemGroups/AssemblyInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15e681dc5470344438ae359e2c142bc0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Builder.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 91a881250aa1b3048a7a54a93b7a7edc 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Builder/ArchSystemsWorldBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cba0c36cb65b56e4d8274c1c6f16f2af 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Builder/ArchSystemsWorldBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups 2 | { 3 | /// 4 | /// Publicly available extensions for the ArchSystemsWorldBuilder 5 | /// 6 | public static class ArchSystemsWorldBuilderExtensions 7 | { 8 | /// 9 | /// Inject a custom group into the world. It allows to create a group with custom parameters. 10 | /// 11 | public static ref ArchSystemsWorldBuilder InjectCustomGroup(ref this ArchSystemsWorldBuilder builder, TGroup group) where TGroup : CustomGroupBase 12 | { 13 | builder.AddCustomGroup(group); 14 | return ref builder; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Builder/ArchSystemsWorldBuilderExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0291129a97f207d45b7ef12142071781 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1b2ee5d5d7c98147b7f33089f054b10 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/InitializationSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.SystemGroups.Throttling; 3 | using Arch.SystemGroups.UnityBridge; 4 | using JetBrains.Annotations; 5 | 6 | namespace Arch.SystemGroups.DefaultSystemGroups 7 | { 8 | /// 9 | /// Updates at the end of the Initialization phase of the player loop 10 | /// 11 | public class InitializationSystemGroup : SystemGroup 12 | { 13 | internal InitializationSystemGroup(List> nodes, [CanBeNull] ISystemGroupThrottler throttler, [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(nodes, throttler, exceptionHandler) 14 | { 15 | } 16 | 17 | internal static readonly InitializationSystemGroup Empty = new (null, null, null); 18 | 19 | public override void Update() 20 | { 21 | Update(TimeProvider.GetInfo()); 22 | } 23 | 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/InitializationSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 901b64e369afe1542a9bdff3a5271639 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PhysicsSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.System; 3 | using Arch.SystemGroups.Throttling; 4 | using Arch.SystemGroups.UnityBridge; 5 | using JetBrains.Annotations; 6 | 7 | namespace Arch.SystemGroups.DefaultSystemGroups 8 | { 9 | /// 10 | /// Updates at the beginning of the FixedUpdate phase of the player loop 11 | /// before all fixed updates 12 | /// 13 | public class PhysicsSystemGroup : SystemGroup 14 | { 15 | internal PhysicsSystemGroup(List> nodes, [CanBeNull] ISystemGroupThrottler throttler, [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(nodes, throttler, exceptionHandler) 16 | { 17 | } 18 | 19 | internal static readonly PhysicsSystemGroup Empty = new (null, null, null); 20 | 21 | public override void Update() 22 | { 23 | Update(TimeProvider.GetFixedInfo()); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PhysicsSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db1589e3797424b4589cc3616648d193 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PostPhysicsSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.SystemGroups.Throttling; 3 | using Arch.SystemGroups.UnityBridge; 4 | using JetBrains.Annotations; 5 | 6 | namespace Arch.SystemGroups.DefaultSystemGroups 7 | { 8 | /// 9 | /// Updates at the end of the FixedUpdate phase of the player loop 10 | /// 11 | public class PostPhysicsSystemGroup : SystemGroup 12 | { 13 | internal PostPhysicsSystemGroup(List> nodes, [CanBeNull] ISystemGroupThrottler throttler, 14 | [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(nodes, throttler, exceptionHandler) 15 | { 16 | } 17 | 18 | internal static readonly PostPhysicsSystemGroup Empty = new(null, null, null); 19 | 20 | public override void Update() 21 | { 22 | Update(TimeProvider.GetFixedInfo()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PostPhysicsSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: debc4ff6ec5837a45b45a31839dce597 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PostRenderingSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.SystemGroups.Throttling; 3 | using Arch.SystemGroups.UnityBridge; 4 | using JetBrains.Annotations; 5 | 6 | namespace Arch.SystemGroups.DefaultSystemGroups 7 | { 8 | /// 9 | /// Updates at the end of the PostLateUpdate phase of the player loop. 10 | /// 11 | public class PostRenderingSystemGroup : SystemGroup 12 | { 13 | internal PostRenderingSystemGroup(List> nodes, [CanBeNull] ISystemGroupThrottler throttler, 14 | [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(nodes, throttler, exceptionHandler) 15 | { 16 | } 17 | 18 | internal static readonly PostRenderingSystemGroup Empty = new (null, null, null); 19 | 20 | public override void Update() 21 | { 22 | Update(TimeProvider.GetInfo()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PostRenderingSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 019205aa9d8622545beafec3ea56df91 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PreRenderingSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.SystemGroups.Throttling; 3 | using Arch.SystemGroups.UnityBridge; 4 | using JetBrains.Annotations; 5 | 6 | namespace Arch.SystemGroups.DefaultSystemGroups 7 | { 8 | /// 9 | /// Updates at the beginning of the PostLateUpdate phase of the player loop. 10 | /// 11 | public class PreRenderingSystemGroup : SystemGroup 12 | { 13 | internal PreRenderingSystemGroup(List> nodes, [CanBeNull] ISystemGroupThrottler throttler, 14 | [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(nodes, throttler, exceptionHandler) 15 | { 16 | } 17 | 18 | internal static readonly PreRenderingSystemGroup Empty = new(null, null, null); 19 | 20 | public override void Update() 21 | { 22 | Update(TimeProvider.GetInfo()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PreRenderingSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b9077eb10de7ad4f93f9ea7ef7cb624 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PresentationSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.System; 3 | using Arch.SystemGroups.Throttling; 4 | using Arch.SystemGroups.UnityBridge; 5 | using JetBrains.Annotations; 6 | 7 | namespace Arch.SystemGroups.DefaultSystemGroups 8 | { 9 | /// 10 | /// Updates at the end of the PreLateUpdate phase of the player loop. 11 | /// 12 | public class PresentationSystemGroup : SystemGroup 13 | { 14 | internal PresentationSystemGroup(List> systems, [CanBeNull] ISystemGroupThrottler throttler, 15 | [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(systems, throttler, exceptionHandler) 16 | { 17 | } 18 | 19 | internal static readonly PresentationSystemGroup Empty = new (null, null, null); 20 | 21 | public override void Update() 22 | { 23 | Update(TimeProvider.GetInfo()); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/PresentationSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a0f46f9a191fab4e8b0c9a6235cc527 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/SimulationSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Arch.SystemGroups.Throttling; 3 | using Arch.SystemGroups.UnityBridge; 4 | using JetBrains.Annotations; 5 | 6 | namespace Arch.SystemGroups.DefaultSystemGroups 7 | { 8 | /// 9 | /// Updates at the end of the Update phase of the player loop 10 | /// 11 | public class SimulationSystemGroup : SystemGroup 12 | { 13 | internal SimulationSystemGroup(List> systems, [CanBeNull] ISystemGroupThrottler throttler, 14 | [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) : base(systems, throttler, exceptionHandler) 15 | { 16 | } 17 | 18 | internal static readonly SimulationSystemGroup Empty = new (null, null, null); 19 | 20 | public override void Update() 21 | { 22 | Update(TimeProvider.GetInfo()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/SimulationSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 350bad26f8a92ba4ab781c23a4fcff10 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/SystemGroupsUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups.DefaultSystemGroups 2 | { 3 | /// 4 | /// System Groups Utility Functions 5 | /// 6 | public static class SystemGroupsUtils 7 | { 8 | /// 9 | /// The number of defined system groups: 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | public const int Count = 6; 18 | } 19 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/DefaultSystemGroups/SystemGroupsUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fd8d97b64a646e649afd099a4f8e0f19 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Descriptors.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b9a0ff45dc470f4f9199ea858ccd14f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Descriptors/Descriptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | /// 5 | /// Describes a group or system 6 | /// 7 | public struct Descriptor 8 | { 9 | /// 10 | /// Constructor 11 | /// 12 | /// Name of the system 13 | /// Is throttling enabled for this system 14 | /// 15 | public Descriptor(string name, bool throttlingEnabled, List subDescriptors = null) 16 | { 17 | Name = name; 18 | ThrottlingEnabled = throttlingEnabled; 19 | SubDescriptors = subDescriptors; 20 | } 21 | 22 | /// 23 | /// Name of the system 24 | /// 25 | public string Name { get; } 26 | 27 | /// 28 | /// Is throttling enabled for this group or system 29 | /// 30 | public bool ThrottlingEnabled { get; } 31 | 32 | 33 | /// 34 | /// Is this descriptor a group of other systems or groups 35 | /// 36 | public bool IsGroup => SubDescriptors != null; 37 | 38 | 39 | /// 40 | /// Is this descriptor a system 41 | /// 42 | public bool IsSystem => SubDescriptors == null; 43 | 44 | 45 | /// 46 | /// A list of sub descriptors if this descriptor is a group 47 | /// 48 | public List SubDescriptors { get; } 49 | } 50 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Descriptors/Descriptor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f241e61f69634396a0c459ba926cb706 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Descriptors/SystemGroupWorldExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using JetBrains.Annotations; 3 | 4 | namespace Arch.SystemGroups.Descriptors 5 | { 6 | /// 7 | /// Extensions for SystemGroupWorld 8 | /// 9 | public static class SystemGroupWorldExtensions 10 | { 11 | /// 12 | /// Generate the descriptor for the SystemGroupWorld 13 | /// 14 | /// 15 | [UsedImplicitly] 16 | public static IReadOnlyList GenerateDescriptors(this SystemGroupWorld world) 17 | { 18 | var descriptors = new List(); 19 | // Foreach system group such as initialization, simulation, presentation 20 | foreach (var worldSystemGroup in world.SystemGroups) 21 | { 22 | // Throttling is not possible on a root level 23 | descriptors.Add(GenerateGroupDescriptor(worldSystemGroup.GetType().Name, false, worldSystemGroup.Nodes)); 24 | } 25 | 26 | return descriptors; 27 | } 28 | 29 | /// 30 | /// Generate the descriptor for the SystemGroup 31 | /// 32 | /// Name of the system group 33 | /// Determines if this group has throttling enabled 34 | /// List of nodes within the system group 35 | private static Descriptor GenerateGroupDescriptor(string name, bool throttlingEnabled, IReadOnlyList> nodes) 36 | { 37 | var descriptors = new List(); 38 | GenerateNestedGroupDescriptors(nodes, descriptors); 39 | return new Descriptor(name,throttlingEnabled,descriptors); 40 | } 41 | 42 | /// 43 | /// Generate the descriptor for the SystemGroupWorld 44 | /// 45 | /// A list of execution nodes 46 | /// A list of descriptors 47 | /// 48 | private static void GenerateNestedGroupDescriptors(IReadOnlyList> nodes, 49 | List descriptors) 50 | { 51 | if(nodes is null) return; 52 | foreach (var node in nodes) 53 | { 54 | if (node.IsGroup) 55 | { 56 | descriptors.Add(GenerateGroupDescriptor(node.CustomGroup.GetType().Name, node.ThrottlingEnabled, node.CustomGroup.Nodes)); 57 | } 58 | else 59 | { 60 | descriptors.Add(new Descriptor(node.System.GetType().Name, node.ThrottlingEnabled)); 61 | } 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Descriptors/SystemGroupWorldExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 36e384939c10e064a9439501b463e997 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2022.2.9f1 5 | 6 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Directory.Build.props.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 802ab0ab081469e429903a57b9d3ca9a 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Arch.SystemGroups/ExecutionNode.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | internal readonly struct ExecutionNode 6 | { 7 | public readonly bool ThrottlingEnabled; 8 | public readonly bool IsGroup; 9 | public readonly ISystem System; 10 | public readonly CustomGroupBase CustomGroup; 11 | 12 | public ExecutionNode(ISystem system, bool throttlingEnabled) 13 | { 14 | ThrottlingEnabled = throttlingEnabled; 15 | System = system; 16 | CustomGroup = null; 17 | IsGroup = false; 18 | } 19 | 20 | public ExecutionNode(CustomGroupBase customGroup, bool throttlingEnabled) 21 | { 22 | IsGroup = true; 23 | ThrottlingEnabled = throttlingEnabled; 24 | CustomGroup = customGroup; 25 | System = null; 26 | } 27 | 28 | public void Initialize() 29 | { 30 | if (IsGroup) 31 | { 32 | CustomGroup.Initialize(); 33 | } 34 | else 35 | { 36 | System.Initialize(); 37 | } 38 | } 39 | 40 | public void Dispose() 41 | { 42 | if (IsGroup) 43 | { 44 | CustomGroup.Dispose(); 45 | } 46 | else 47 | { 48 | System.Dispose(); 49 | } 50 | } 51 | 52 | public void BeforeUpdate(in T t, bool throttle) 53 | { 54 | if (throttle && ThrottlingEnabled) 55 | return; 56 | 57 | if (IsGroup) 58 | { 59 | CustomGroup.BeforeUpdate(t, throttle); 60 | } 61 | else 62 | { 63 | System.BeforeUpdate(t); 64 | } 65 | } 66 | 67 | public void Update(in T t, bool throttle) 68 | { 69 | if (throttle && ThrottlingEnabled) 70 | return; 71 | 72 | if (IsGroup) 73 | { 74 | CustomGroup.Update(t, throttle); 75 | } 76 | else 77 | { 78 | System.Update(t); 79 | } 80 | } 81 | 82 | public void AfterUpdate(in T t, bool throttle) 83 | { 84 | if (throttle && ThrottlingEnabled) 85 | return; 86 | 87 | if (IsGroup) 88 | { 89 | CustomGroup.AfterUpdate(t, throttle); 90 | } 91 | else 92 | { 93 | System.AfterUpdate(t); 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/ExecutionNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a573707f3bb016c4c9a7cd95ce356ec5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6aaeda95f0b82d14faff948db5ed144b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups/CustomGroupBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Arch.System; 4 | using Arch.SystemGroups.Metadata; 5 | using UnityEngine.Pool; 6 | 7 | namespace Arch.SystemGroups 8 | { 9 | /// 10 | /// The base class that can be used to provide custom behaviour for a group 11 | /// 12 | /// 13 | public abstract class CustomGroupBase 14 | { 15 | /// 16 | /// Creates an empty group 17 | /// 18 | protected CustomGroupBase() 19 | { 20 | } 21 | 22 | /// 23 | /// Creates a group from the collection from which a pooled instance of the list will be created 24 | /// 25 | protected CustomGroupBase(IEnumerable> systems, bool throttlingEnabled) 26 | { 27 | Nodes = ListPool>.Get(); 28 | AddRange(systems.Select(s => new ExecutionNode(s, throttlingEnabled))); 29 | } 30 | 31 | internal List> Nodes { get; private set; } 32 | 33 | /// 34 | /// Override to provide Dispose behaviour, you can use as the default implementation 35 | /// 36 | public abstract void Dispose(); 37 | 38 | /// 39 | /// Override to provide initialization behaviour, you can use as the default 40 | /// implementation 41 | /// 42 | public abstract void Initialize(); 43 | 44 | /// 45 | /// Override to provide BeforeUpdate, you can use as the default implementation 46 | /// 47 | /// 48 | /// Indicates that the current invocation is throttled 49 | public abstract void BeforeUpdate(in T t, bool throttle); 50 | 51 | /// 52 | /// Override to provide Update behaviour, you can use as the default implementation 53 | /// 54 | /// 55 | /// Indicates that the current invocation is throttled 56 | public abstract void Update(in T t, bool throttle); 57 | 58 | /// 59 | /// Override to provide AfterUpdate behaviour, you can use as the default 60 | /// implementation 61 | /// 62 | /// Indicates that the current invocation is throttled 63 | /// 64 | public abstract void AfterUpdate(in T t, bool throttle); 65 | 66 | /// 67 | /// Adds systems to the group 68 | /// 69 | /// 70 | internal void AddRange(IEnumerable> systems) 71 | { 72 | Nodes.AddRange(systems); 73 | } 74 | 75 | internal void SetSystems(List> systems) 76 | { 77 | Nodes = systems; 78 | } 79 | 80 | /// 81 | /// Initialize all systems in the group 82 | /// 83 | protected void InitializeInternal() 84 | { 85 | foreach (var node in Nodes) 86 | node.Initialize(); 87 | } 88 | 89 | /// 90 | /// Dispose all systems in the group 91 | /// 92 | protected void DisposeInternal() 93 | { 94 | foreach (var node in Nodes) 95 | node.Dispose(); 96 | 97 | ListPool>.Release(Nodes); 98 | } 99 | 100 | /// 101 | /// Update all systems 102 | /// 103 | /// Delta time 104 | /// Current update is throttled 105 | protected void BeforeUpdateInternal(in T t, bool throttle) 106 | { 107 | for (var index = 0; index < Nodes.Count; ++index) 108 | Nodes[index].BeforeUpdate(in t, throttle); 109 | } 110 | 111 | /// 112 | /// Update all systems 113 | /// 114 | /// Delta time 115 | /// Current update is throttled 116 | protected void UpdateInternal(in T t, bool throttle) 117 | { 118 | for (var index = 0; index < Nodes.Count; ++index) 119 | Nodes[index].Update(in t, throttle); 120 | } 121 | 122 | /// 123 | /// Update all systems 124 | /// 125 | /// Delta time 126 | /// Current update is throttled 127 | protected void AfterUpdateInternal(in T t, bool throttle) 128 | { 129 | for (var index = 0; index < Nodes.Count; ++index) 130 | Nodes[index].AfterUpdate(in t, throttle); 131 | } 132 | 133 | /// 134 | /// The metadata of the group in an abstract form 135 | /// 136 | /// 137 | protected abstract AttributesInfoBase GetMetadataInternal(); 138 | } 139 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups/CustomGroupBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 394ee11ed2a497242a9f853086a65dda 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups/DefaultGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups 2 | { 3 | /// 4 | /// Similar to `Arch.System.Group` but with better API that allows pooling 5 | /// 6 | /// 7 | public abstract class DefaultGroup : CustomGroupBase 8 | { 9 | /// 10 | /// Creates an empty group, for auto-generated code only, 11 | /// Don't invoke it manually 12 | /// 13 | protected DefaultGroup() 14 | { 15 | } 16 | 17 | /// 18 | /// Initialize all systems in the group 19 | /// 20 | public override void Initialize() 21 | { 22 | InitializeInternal(); 23 | } 24 | 25 | /// 26 | /// Dispose all systems in the group 27 | /// 28 | public override void Dispose() 29 | { 30 | DisposeInternal(); 31 | } 32 | 33 | /// 34 | /// To comply with Arch.System.ISystem 35 | /// 36 | public override void BeforeUpdate(in T t, bool throttle) 37 | { 38 | BeforeUpdateInternal(in t, throttle); 39 | } 40 | 41 | /// 42 | /// To comply with Arch.System.ISystem 43 | /// 44 | public override void Update(in T t, bool throttle) 45 | { 46 | UpdateInternal(in t, throttle); 47 | } 48 | 49 | /// 50 | /// To comply with Arch.System.ISystem 51 | /// 52 | public override void AfterUpdate(in T t, bool throttle) 53 | { 54 | AfterUpdateInternal(in t, throttle); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups/DefaultGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f8662dff74f1858499643a6c2f5385ec 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups/GroupNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | /// 6 | /// Indicates that the group was not injected into the builder 7 | /// but there are systems included in it. 8 | /// 9 | public class GroupNotFoundException : Exception 10 | { 11 | private readonly Type _type; 12 | 13 | internal GroupNotFoundException(Type type) 14 | { 15 | _type = type; 16 | } 17 | 18 | /// 19 | /// 20 | /// 21 | public override string Message => $"Group {_type} was not injected into the builder"; 22 | } 23 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Groups/GroupNotFoundException.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 36a88025b7da8794ab65c0232c9ffe86 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/ISystemGroupExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | /// 6 | /// Provides exceptions handling on the level of the 7 | /// 8 | public interface ISystemGroupExceptionHandler 9 | { 10 | /// 11 | /// Action to tell the System Group how to behave after the exception 12 | /// 13 | public enum Action : byte 14 | { 15 | /// 16 | /// Continue execution of the system 17 | /// 18 | Continue, 19 | 20 | /// 21 | /// Put the system group into the Error state and stops the execution 22 | /// 23 | Suspend, 24 | 25 | /// 26 | /// Dispose the system group and stops the execution 27 | /// 28 | Dispose 29 | } 30 | 31 | /// 32 | /// Handles the exception thrown by the system group, at some point the execution of the system group 33 | /// should be suspended to prevent exceptions flood 34 | /// 35 | /// Exception 36 | /// System Group Type 37 | /// An action to tell the System Group how to behave after 38 | Action Handle(Exception exception, Type systemGroupType); 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/ISystemGroupExceptionHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b8882a2c949ae7a4ab1763629f4a2793 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Metadata.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 859d3d8231ffd6e44b2b45e4071b681c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Metadata/AttributesInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Arch.SystemGroups.Metadata 5 | { 6 | /// 7 | /// Generated attributes info, allows to avoid reflection if such access is needed 8 | /// 9 | public abstract class AttributesInfoBase 10 | { 11 | /// 12 | /// reflection, will be null for system groups 13 | /// 14 | public abstract Type UpdateInGroup { get; } 15 | 16 | /// 17 | /// Metadata of the group this system belongs to, will be null for system groups 18 | /// 19 | public abstract AttributesInfoBase GroupMetadata { get; } 20 | 21 | /// 22 | /// Get first attribute of type 23 | /// 24 | /// Type of attribute 25 | /// Null if such attribute is not defined for the class 26 | public abstract T GetAttribute() where T : Attribute; 27 | 28 | /// 29 | /// Get all attributes of type 30 | /// 31 | /// Type of attribute 32 | /// An empty list if no attributes are found 33 | public abstract IReadOnlyList GetAttributes() where T : Attribute; 34 | } 35 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Metadata/AttributesInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 84baf46bc7288ff41bfedefe1e63fc38 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Metadata/SystemGroupAttributesInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Arch.SystemGroups.Metadata 5 | { 6 | /// 7 | /// Dummy attributes info for system groups 8 | /// 9 | public class SystemGroupAttributesInfo : AttributesInfoBase 10 | { 11 | /// 12 | /// Instance shared between all System Groups as they provide no attributes data 13 | /// 14 | public static readonly SystemGroupAttributesInfo Instance = new(); 15 | 16 | /// 17 | /// Returns null 18 | /// 19 | public override Type UpdateInGroup => null; 20 | 21 | /// 22 | /// Returns null 23 | /// 24 | public override AttributesInfoBase GroupMetadata => null; 25 | 26 | /// 27 | /// Returns null 28 | /// 29 | /// 30 | /// 31 | public override T GetAttribute() => null; 32 | 33 | /// 34 | /// Returns an empty array 35 | /// 36 | /// 37 | /// 38 | public override IReadOnlyList GetAttributes() => Array.Empty(); 39 | } 40 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Metadata/SystemGroupAttributesInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5a9690ec3c570d04fb5a9f221d55cef6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3940d4dacd8d44640b17c9a3e4c0ee43 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 617b67f3228e0da49ab8af7e913305b8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/ISystemGroupAggregate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | /// 6 | /// Non-generic interface for 7 | /// 8 | public interface ISystemGroupAggregate 9 | { 10 | /// 11 | /// Count of system groups 12 | /// 13 | int Count { get; } 14 | 15 | /// 16 | /// This function is called from Unity Player Loop 17 | /// 18 | void TriggerUpdate(); 19 | 20 | /// 21 | /// Remove a system group from the aggregate 22 | /// 23 | void Remove(SystemGroup systemGroup); 24 | } 25 | 26 | /// 27 | /// Defines a way of aggregating system groups of the same type 28 | /// Additional Data set per world basis 29 | /// 30 | public interface ISystemGroupAggregate : ISystemGroupAggregate 31 | { 32 | /// 33 | /// Add a system group to the aggregate 34 | /// 35 | void Add(in T data, SystemGroup systemGroup); 36 | 37 | /// 38 | /// Factory for SystemGroupAggregate 39 | /// 40 | interface IFactory : ISystemGroupAggregateFactory 41 | { 42 | ISystemGroupAggregate ISystemGroupAggregateFactory.Create(Type systemGroupType) 43 | { 44 | return Create(systemGroupType); 45 | } 46 | 47 | /// 48 | /// Creates a new instance of SystemGroupAggregate. 49 | /// Called once per type of SystemGroup 50 | /// 51 | /// 52 | new ISystemGroupAggregate Create(Type systemGroupType); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/ISystemGroupAggregate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 609598de4183bf744a8b39f0e475ba0b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/ISystemGroupAggregateFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | /// 6 | /// Base interface for all system group aggregates 7 | /// 8 | public interface ISystemGroupAggregateFactory 9 | { 10 | internal ISystemGroupAggregate Create(Type systemGroupType); 11 | } 12 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/ISystemGroupAggregateFactory.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4db902ed20c680b43a83426af267b61e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/OrderedSystemGroupAggregate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | /// 6 | /// Executes system groups in a specific order 7 | /// 8 | public class OrderedSystemGroupAggregate : ISystemGroupAggregate 9 | { 10 | private readonly SortedList _sortedList; 11 | 12 | /// 13 | /// Creates a new instance of OrderedSystemGroupAggregate with the specified comparer 14 | /// 15 | /// Comparer should never return 0 for different system groups as it is forbidden by . 16 | /// Specify "debounceEqualValues" to force it 17 | /// If True overrides the behaviour of comparer in a way that it never returns 0 18 | /// Initial capacity of the underlying collection 19 | public OrderedSystemGroupAggregate(IComparer comparer, bool debounceEqualValues = false, int initialCapacity = 16) 20 | { 21 | if (debounceEqualValues) 22 | comparer = new DebouncedComparer(comparer); 23 | 24 | _sortedList = new SortedList(initialCapacity, comparer); 25 | } 26 | 27 | /// 28 | /// 29 | /// 30 | public int Count => _sortedList.Count; 31 | 32 | internal IList Values => _sortedList.Values; 33 | 34 | /// 35 | /// 36 | /// 37 | public void TriggerUpdate() 38 | { 39 | for (var i = 0; i < _sortedList.Values.Count; i++) 40 | { 41 | var systemGroup = _sortedList.Values[i]; 42 | systemGroup.Update(); 43 | } 44 | } 45 | 46 | /// 47 | /// 48 | /// 49 | public void Add(in T data, SystemGroup systemGroup) 50 | { 51 | _sortedList.Add(data, systemGroup); 52 | } 53 | 54 | /// 55 | /// 56 | /// 57 | public void Remove(SystemGroup systemGroup) 58 | { 59 | var index = _sortedList.Values.IndexOf(systemGroup); 60 | if (index > -1) 61 | _sortedList.RemoveAt(index); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/OrderedSystemGroupAggregate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 88d7ec54280b5e04bac5aa628eb24805 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/SystemGroupAggregate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Arch.SystemGroups 5 | { 6 | /// 7 | /// Contains the list of system groups of the same type 8 | /// 9 | internal class SystemGroupAggregate : ISystemGroupAggregate 10 | { 11 | internal class Factory : ISystemGroupAggregate.IFactory 12 | { 13 | internal static readonly Factory Instance = new(); 14 | 15 | public ISystemGroupAggregate Create(Type systemGroupType) => new SystemGroupAggregate(systemGroupType); 16 | } 17 | 18 | private readonly List _systemGroups = new(16); 19 | 20 | /// 21 | /// For debugging purpose only 22 | /// 23 | internal readonly Type GroupType; 24 | 25 | public SystemGroupAggregate(Type groupType) 26 | { 27 | GroupType = groupType; 28 | } 29 | 30 | public int Count => _systemGroups.Count; 31 | 32 | public void TriggerUpdate() 33 | { 34 | for (var i = 0; i < _systemGroups.Count; i++) 35 | { 36 | _systemGroups[i].Update(); 37 | } 38 | } 39 | 40 | public void Add(in None none, SystemGroup systemGroup) 41 | { 42 | _systemGroups.Add(systemGroup); 43 | } 44 | 45 | public void Remove(SystemGroup systemGroup) 46 | { 47 | _systemGroups.Remove(systemGroup); 48 | } 49 | 50 | internal struct None 51 | { 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/SystemGroupAggregate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 35e5bf0465a451a4c8c923864ced48ca 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/SystemGroupAggregateCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Arch.SystemGroups.DefaultSystemGroups; 4 | 5 | namespace Arch.SystemGroups 6 | { 7 | /// 8 | /// Caches the system group aggregates by type of system group, produced for each 9 | /// 10 | internal class SystemGroupAggregateCache 11 | { 12 | private readonly Dictionary _aggregates = new (SystemGroupsUtils.Count); 13 | 14 | internal void GetAllAggregates(List results) where TAggregate : ISystemGroupAggregate 15 | { 16 | foreach (var aggregate in _aggregates.Values) 17 | results.Add((TAggregate) aggregate); 18 | } 19 | 20 | public int Count => _aggregates.Count; 21 | 22 | public ISystemGroupAggregate Add(Type systemGroupType, ISystemGroupAggregate.IFactory factory) 23 | { 24 | var aggregate = factory.Create(systemGroupType); 25 | _aggregates.Add(systemGroupType, aggregate); 26 | return aggregate; 27 | } 28 | 29 | public void Remove(IPlayerLoop playerLoop, Type systemGroupType, SystemGroup systemGroup) 30 | { 31 | if (_aggregates.TryGetValue(systemGroupType, out var aggregate)) 32 | { 33 | aggregate.Remove(systemGroup); 34 | if (aggregate.Count == 0) 35 | { 36 | _aggregates.Remove(systemGroupType); 37 | playerLoop.RemoveAggregate(aggregate); 38 | } 39 | } 40 | } 41 | 42 | public bool TryGetValue(Type systemGroupType, out ISystemGroupAggregate aggregate) => _aggregates.TryGetValue(systemGroupType, out aggregate); 43 | 44 | public bool TryGetValue(Type systemGroupType, out ISystemGroupAggregate aggregate) 45 | { 46 | if (_aggregates.TryGetValue(systemGroupType, out var value)) 47 | { 48 | aggregate = (ISystemGroupAggregate) value; 49 | return true; 50 | } 51 | 52 | aggregate = default; 53 | return false; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/Aggregation/SystemGroupAggregateCache.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f747f0e5770656645ae055896715f951 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/DebouncedComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | internal class DebouncedComparer : IComparer 6 | { 7 | private readonly IComparer _inner; 8 | 9 | public DebouncedComparer(IComparer inner) 10 | { 11 | _inner = inner; 12 | } 13 | 14 | public int Compare(T x, T y) 15 | { 16 | var result = _inner.Compare(x, y); 17 | return result == 0 ? 1 : result; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/DebouncedComparer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ffc0f79083ec3b14281194464acf100a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/IPlayerLoop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | /// 6 | /// Abstraction needed for Mocking or providing a custom implementation of injection into the Player Loop 7 | /// 8 | public interface IPlayerLoop 9 | { 10 | /// 11 | /// Called before all other methods once for each world 12 | /// 13 | void OnWorldStartAppending(); 14 | 15 | /// 16 | /// Called after all and 17 | /// 18 | void OnWorldEndAppending(); 19 | 20 | /// 21 | /// Adds an aggregate of system groups to the player loop. It is called only once upon the first mentioning of . 22 | /// 23 | void AddAggregate(Type systemGroupType, ISystemGroupAggregate aggregate); 24 | 25 | /// 26 | /// Removes the given system group from the Unity Player Loop. 27 | /// 28 | /// 29 | void RemoveAggregate(ISystemGroupAggregate aggregate); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/IPlayerLoop.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ae9195734a75d594ebe16a446579088a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/PlayerLoopAddMode.cs: -------------------------------------------------------------------------------- 1 | namespace Arch.SystemGroups 2 | { 3 | /// 4 | /// Determines whether the system should be added to the beginning or the end of the step of the player loop 5 | /// 6 | internal enum PlayerLoopAddMode : byte 7 | { 8 | /// 9 | /// Add the system to the beginning of the step 10 | /// 11 | Prepend, 12 | 13 | /// 14 | /// Add the system to the end of the step 15 | /// 16 | Append 17 | } 18 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/PlayerLoopAddMode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8a962d05fc97ca40ace0eb99da35a38 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/PlayerLoopHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dbf68b24c2c644647972f22f203ff543 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopHelper/UnityPlayerLoop.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 98962eb5a92c99e418bb56472b4fc511 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopSystem.cs: -------------------------------------------------------------------------------- 1 | using Arch.System; 2 | using Arch.SystemGroups.Metadata; 3 | 4 | namespace Arch.SystemGroups 5 | { 6 | /// 7 | /// The base system for all systems that are executed in the player loop 8 | /// 9 | /// 10 | public abstract class PlayerLoopSystem : BaseSystem 11 | { 12 | /// 13 | /// Default constructor for a player loop system 14 | /// 15 | /// 16 | protected PlayerLoopSystem(TWorld world) : base(world) 17 | { 18 | } 19 | 20 | /// 21 | /// The metadata of the system in an abstract form 22 | /// 23 | /// 24 | protected abstract AttributesInfoBase GetMetadataInternal(); 25 | } 26 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/PlayerLoopSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc1d816c00b389a4d9ed5ada046a133a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Arch.SystemGroups.Metadata; 4 | using Arch.SystemGroups.Throttling; 5 | using Arch.SystemGroups.UnityBridge; 6 | using JetBrains.Annotations; 7 | using UnityEngine.Pool; 8 | 9 | namespace Arch.SystemGroups 10 | { 11 | /// 12 | /// Denotes a root group connected to a specific phase of the player loop. 13 | /// By default updated by the scaled deltaTime. 14 | /// If Unscaled delta time is needed consider using Time.unscaledXXX manually. 15 | /// 16 | public abstract class SystemGroup : IDisposable 17 | { 18 | /// 19 | /// State of the system group 20 | /// 21 | public enum State : byte 22 | { 23 | /// 24 | /// Initialized was not called yet 25 | /// 26 | NotInitialized, 27 | 28 | /// 29 | /// Up and Running 30 | /// 31 | Active, 32 | 33 | /// 34 | /// The execution of update functions is suspended but the group is not disposed 35 | /// 36 | Suspended, 37 | 38 | /// 39 | /// Disposed was executed 40 | /// 41 | Disposed 42 | } 43 | 44 | /// 45 | /// An empty shared instance of attributes metadata 46 | /// 47 | public static SystemGroupAttributesInfo Metadata = SystemGroupAttributesInfo.Instance; 48 | 49 | private readonly ISystemGroupExceptionHandler _exceptionHandler; 50 | 51 | private readonly ISystemGroupThrottler _throttler; 52 | 53 | private readonly Type _type; 54 | 55 | internal SystemGroup(List> nodes, [CanBeNull] ISystemGroupThrottler throttler, 56 | [CanBeNull] ISystemGroupExceptionHandler exceptionHandler) 57 | { 58 | Nodes = nodes; 59 | _throttler = throttler; 60 | _exceptionHandler = exceptionHandler; 61 | _type = GetType(); 62 | } 63 | 64 | internal State CurrentState { get; private set; } = State.NotInitialized; 65 | 66 | internal List> Nodes { get; } 67 | 68 | /// 69 | /// Dispose all systems and release the list allocated for them. 70 | /// After the dispose is called the instance of the group is no longer usable. 71 | /// 72 | public void Dispose() 73 | { 74 | if (Nodes == null) return; 75 | if (CurrentState == State.Disposed) return; 76 | 77 | foreach (var system in Nodes) 78 | system.Dispose(); 79 | ListPool>.Release(Nodes); 80 | CurrentState = State.Disposed; 81 | } 82 | 83 | /// 84 | /// Update all nodes in the group 85 | /// 86 | public abstract void Update(); 87 | 88 | private protected void Update(in TimeProvider.Info timeInfo) 89 | { 90 | if (Nodes == null) return; 91 | 92 | if (Nodes.Count == 0) return; 93 | 94 | if (CurrentState != State.Active) return; 95 | 96 | var throttle = _throttler != null && _throttler.ShouldThrottle(_type, in timeInfo); 97 | 98 | try 99 | { 100 | foreach (var system in Nodes) 101 | { 102 | system.BeforeUpdate(in timeInfo.DeltaTime, throttle); 103 | } 104 | 105 | foreach (var system in Nodes) 106 | { 107 | system.Update(in timeInfo.DeltaTime, throttle); 108 | } 109 | 110 | foreach (var system in Nodes) 111 | { 112 | system.AfterUpdate(in timeInfo.DeltaTime, throttle); 113 | } 114 | } 115 | catch (Exception e) 116 | { 117 | if (_exceptionHandler == null) 118 | throw; 119 | 120 | switch (_exceptionHandler.Handle(e, _type)) 121 | { 122 | case ISystemGroupExceptionHandler.Action.Suspend: 123 | CurrentState = State.Suspended; 124 | break; 125 | case ISystemGroupExceptionHandler.Action.Dispose: 126 | Dispose(); 127 | break; 128 | } 129 | } 130 | 131 | _throttler?.OnSystemGroupUpdateFinished(_type, throttle); 132 | } 133 | 134 | internal void Initialize() 135 | { 136 | if (Nodes == null) return; 137 | 138 | CurrentState = State.Active; 139 | 140 | foreach (var system in Nodes) 141 | system.Initialize(); 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b8268679fd75c1478fce5a8f16ac241 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemGroupWorld.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Arch.SystemGroups 5 | { 6 | /// 7 | /// An entry point to the systems connected to the Unity Player Loop. 8 | /// 9 | public class SystemGroupWorld : IDisposable 10 | { 11 | private readonly IPlayerLoop _playerLoop; 12 | 13 | // Aggregate factory is used to create the aggregate for each system group type, 14 | // acts as a key in the SystemGroupAggregateCache 15 | private readonly ISystemGroupAggregateFactory _aggregateFactory; 16 | 17 | internal IReadOnlyList SystemGroups { get; } 18 | 19 | internal SystemGroupWorld(IReadOnlyList systemGroups, IPlayerLoop playerLoop, ISystemGroupAggregateFactory aggregateFactory) 20 | { 21 | _playerLoop = playerLoop; 22 | _aggregateFactory = aggregateFactory; 23 | SystemGroups = systemGroups; 24 | } 25 | 26 | /// 27 | /// Recursively Initialize all systems in the world according to their execution order 28 | /// 29 | public void Initialize() 30 | { 31 | for (var i = 0; i < SystemGroups.Count; i++) 32 | { 33 | SystemGroups[i].Initialize(); 34 | } 35 | } 36 | 37 | /// 38 | /// Recursively Dispose all systems in the world according to their execution order. 39 | /// Remove all systems from the player loop 40 | /// 41 | public void Dispose() 42 | { 43 | for (var i = 0; i < SystemGroups.Count; i++) 44 | { 45 | SystemGroups[i].Dispose(); 46 | _playerLoop.RemoveFromPlayerLoop(_aggregateFactory, SystemGroups[i]); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemGroupWorld.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 22255010444a7794ab7f07e800228e28 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1bcd6a8c3847b754794a98608eb4f2f2 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies/ArchSystemsSorter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Arch.System; 4 | using UnityEngine.Pool; 5 | 6 | namespace Arch.SystemGroups 7 | { 8 | /// 9 | /// Sorts systems. Used by auto-generated code. Consider ignoring it 10 | /// 11 | public static class ArchSystemsSorter 12 | { 13 | /// 14 | /// Adds a dependency edge to the graph. 15 | /// Supports redundancies. 16 | /// Circular dependencies will be resolved on the final stage of the graph traversal 17 | /// 18 | /// System that should be updated before 19 | /// System that should be updated after 20 | /// Storage of edges 21 | public static void AddEdge(Type from, Type to, Dictionary> edges) 22 | { 23 | if (!edges.TryGetValue(from, out var list)) 24 | edges[from] = list = ListPool.Get(); 25 | list.Add(to); 26 | } 27 | 28 | /// 29 | /// Called from auto-generated code, validate edges belong to the same group 30 | /// 31 | public static void ValidateEdge(List disconnectedDependencies, Type declaredOn, Type group, Type dependencyType, Type dependencyGroup) 32 | { 33 | if (group != dependencyGroup) 34 | disconnectedDependencies.Add(new DisconnectedDependenciesInfo.WrongTypeBinding(dependencyType, declaredOn)); 35 | } 36 | 37 | internal static List> SortSystems(Dictionary> systems, Dictionary> edges) 38 | { 39 | var result = ListPool>.Get(); 40 | 41 | var visited = HashSetPool.Get(); 42 | 43 | foreach (var system in systems.Keys) 44 | { 45 | // Circular dependencies on the root will be found in the second cycle of the DFS Traversal 46 | DFSTraversal(systems, system, visited, result, edges); 47 | } 48 | 49 | HashSetPool.Release(visited); 50 | 51 | result.Reverse(); 52 | 53 | return result; 54 | } 55 | 56 | private static Type DFSTraversal(Dictionary> systems, Type currentVertex, 57 | HashSet visited, List> result, Dictionary> edges) 58 | { 59 | if (visited.Add(currentVertex)) 60 | { 61 | if (edges.TryGetValue(currentVertex, out var currentVertexEdges)) 62 | { 63 | foreach (var vertex in currentVertexEdges) 64 | { 65 | // the current vertex may not belong to the current hierarchy 66 | if (!systems.ContainsKey(vertex)) 67 | continue; 68 | 69 | var circularDependency = DFSTraversal(systems, vertex, visited, result, edges); 70 | if (circularDependency == currentVertex) 71 | throw new InvalidOperationException($"Circular dependency detected on a path: {currentVertex} -> {vertex} -> ..."); 72 | } 73 | } 74 | 75 | // Instead of adding to the head (that is much more expensive) we add to the tail and reverse the list 76 | result.Add(systems[currentVertex]); 77 | } 78 | 79 | // return the type that is a potential circular dependency 80 | return currentVertex; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies/ArchSystemsSorter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 319693f23606d764993ccfe4399d204d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies/DisconnectedDependenciesFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Arch.SystemGroups 6 | { 7 | /// 8 | /// Wrong dependencies were detected on traversal of the dependency graph 9 | /// 10 | public class DisconnectedDependenciesFoundException : Exception 11 | { 12 | private readonly IReadOnlyList _disconnectedDependencies; 13 | 14 | internal DisconnectedDependenciesFoundException(IReadOnlyList disconnectedDependencies) 15 | { 16 | _disconnectedDependencies = disconnectedDependencies; 17 | } 18 | 19 | public override string Message => string.Join(Environment.NewLine, _disconnectedDependencies.Select(PrintDependencyInfo)); 20 | 21 | private static string PrintDependencyInfo(DisconnectedDependenciesInfo disconnectedDependenciesInfo) 22 | { 23 | static string PrintWrongBinding(DisconnectedDependenciesInfo.WrongTypeBinding wrongTypeBinding) 24 | { 25 | return wrongTypeBinding.DeclaredOn == null 26 | ? $"{wrongTypeBinding.DependencyType.Name} (declared on a group)" 27 | : $"{wrongTypeBinding.DependencyType.Name} (declared on {wrongTypeBinding.DeclaredOn.Name})"; 28 | } 29 | 30 | return $"The following dependencies don't belong to the group {disconnectedDependenciesInfo.GroupType.Name}: " + 31 | $"{string.Join(", ", disconnectedDependenciesInfo.WrongDependencies.Select(PrintWrongBinding))}"; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies/DisconnectedDependenciesFoundException.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe64d6ec4de154a4fa9dbdf633aae2a8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies/DisconnectedDependenciesInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using JetBrains.Annotations; 4 | 5 | namespace Arch.SystemGroups 6 | { 7 | /// 8 | /// Contains information about the specified dependencies 9 | /// which do not belong to the given hierarchy 10 | /// 11 | public readonly struct DisconnectedDependenciesInfo 12 | { 13 | /// 14 | /// Denotes a pair of dependency type and the type it was declared on 15 | /// 16 | public readonly struct WrongTypeBinding 17 | { 18 | /// 19 | /// Type of the dependency 20 | /// 21 | public readonly Type DependencyType; 22 | 23 | /// 24 | /// Type the dependency was declared on 25 | /// 26 | [CanBeNull] public readonly Type DeclaredOn; 27 | 28 | internal WrongTypeBinding(Type dependencyType, Type declaredOn) 29 | { 30 | DependencyType = dependencyType; 31 | DeclaredOn = declaredOn; 32 | } 33 | } 34 | 35 | /// 36 | /// Type of the group 37 | /// 38 | public readonly Type GroupType; 39 | 40 | /// 41 | /// 42 | /// 43 | public readonly IReadOnlyList WrongDependencies; 44 | 45 | internal DisconnectedDependenciesInfo(Type groupType, IReadOnlyList wrongDependencies) 46 | { 47 | GroupType = groupType; 48 | WrongDependencies = wrongDependencies; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsDependencies/DisconnectedDependenciesInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b55b3b7da2ae7dd4fb4caa280c76de9c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsUpdateOrder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups 4 | { 5 | // Interface used for constraining generic functions on Attributes 6 | // which control system update, creation, or destruction order 7 | internal interface ISystemOrderAttribute 8 | { 9 | Type SystemType { get; } 10 | } 11 | 12 | /// 13 | /// Apply to a system to specify an update ordering constraint with another system in the same or . 14 | /// 15 | /// Updating before or after a system constrains the scheduler ordering of these systems within a ComponentSystemGroup. 16 | /// Both the before and after systems must be a members of the same ComponentSystemGroup. 17 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] 18 | public class UpdateBeforeAttribute : Attribute, ISystemOrderAttribute 19 | { 20 | /// 21 | /// Specify a system which the tagged system must update before. 22 | /// 23 | /// The target system which the tagged system must update before. This system must be 24 | /// a member of the same or . 25 | /// Thrown if the system type is empty. 26 | public UpdateBeforeAttribute(Type systemType) 27 | { 28 | SystemType = systemType ?? throw new ArgumentNullException(nameof(systemType)); 29 | } 30 | 31 | /// 32 | /// The type of the target system, which the tagged system must update before. 33 | /// 34 | public Type SystemType { get; } 35 | } 36 | 37 | /// 38 | /// Apply to a system to specify an update ordering constraint with another system in the same or . 39 | /// 40 | /// Updating before or after a system constrains the scheduler ordering of these systems within a ComponentSystemGroup. 41 | /// Both the before and after systems must be a members of the same ComponentSystemGroup. 42 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] 43 | public class UpdateAfterAttribute : Attribute, ISystemOrderAttribute 44 | { 45 | /// 46 | /// Specify a system which the tagged system must update after. 47 | /// 48 | /// The target system which the tagged system must update after. This system must be 49 | /// a member of the same or . 50 | /// Thrown if the system type is empty. 51 | public UpdateAfterAttribute(Type systemType) 52 | { 53 | SystemType = systemType ?? throw new ArgumentNullException(nameof(systemType)); 54 | } 55 | 56 | /// 57 | /// The type of the target system, which the tagged system must update after. 58 | /// 59 | public Type SystemType { get; } 60 | } 61 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/SystemsUpdateOrder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 04b2cd3142926454e809c31c87d56b57 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Throttling.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bb7a027387c0aee419413096cab62fff 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Throttling/ISystemGroupThrottler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Arch.SystemGroups.UnityBridge; 3 | 4 | namespace Arch.SystemGroups.Throttling 5 | { 6 | /// 7 | /// Provides a way to throttle systems in the root system group, reused for different system groups 8 | /// 9 | public interface ISystemGroupThrottler 10 | { 11 | /// 12 | /// Called when the system group begins to update within the Unity Player Loop 13 | /// 14 | /// Type of the system group 15 | /// Information about time 16 | bool ShouldThrottle(Type systemGroupType, in TimeProvider.Info timeInfo); 17 | 18 | /// 19 | /// Called when the whole system group finishes its update, irrespective of whether it was throttled or not 20 | /// 21 | /// Type of the system group 22 | /// The execution was throttled 23 | void OnSystemGroupUpdateFinished(Type systemGroupType, bool wasThrottled); 24 | } 25 | 26 | /// 27 | /// Throttler dedicated to the system groups based on non-fixed updates 28 | /// 29 | public interface IUpdateBasedSystemGroupThrottler : ISystemGroupThrottler 30 | { 31 | } 32 | 33 | /// 34 | /// Throttler dedicated to the system groups based on fixed updates 35 | /// 36 | public interface IFixedUpdateBasedSystemGroupThrottler : ISystemGroupThrottler 37 | { 38 | } 39 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Throttling/ISystemGroupThrottler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 39c82003a7012b74e8880156867c32a0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/Throttling/ThrottlingEnabledAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Arch.SystemGroups.Throttling 4 | { 5 | /// 6 | /// Indicates that the system or the group can throttle 7 | /// If the group is marked by this attribute all its direct and transitive children will inherit it 8 | /// 9 | [AttributeUsage(AttributeTargets.Class)] 10 | public class ThrottlingEnabledAttribute : Attribute 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/Throttling/ThrottlingEnabledAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2729836d552c7394298c49fd8704c8b2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/UnityBridge.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 34600cc439d1a19469cff16fcde4e6a6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Arch.SystemGroups/UnityBridge/TimeProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Arch.SystemGroups.UnityBridge 4 | { 5 | /// 6 | /// Can't call Unity API without Unity running 7 | /// 8 | public static class TimeProvider 9 | { 10 | /// 11 | /// Information about time, contains Fixed Time for Physics Systems Groups and Time for the rest 12 | /// 13 | [StructLayout(LayoutKind.Sequential)] 14 | public readonly struct Info 15 | { 16 | /// 17 | /// or 18 | /// 19 | public readonly float DeltaTime; 20 | 21 | /// 22 | /// or 23 | /// 24 | public readonly float CurrentUnscaledTime; 25 | 26 | /// 27 | /// or 28 | /// 29 | public readonly float CurrentScaledTime; 30 | 31 | /// 32 | /// 33 | /// 34 | public readonly float Realtime; 35 | 36 | internal Info(float deltaTime, float currentUnscaledTime, float currentScaledTime, float realtime) 37 | { 38 | DeltaTime = deltaTime; 39 | CurrentUnscaledTime = currentUnscaledTime; 40 | CurrentScaledTime = currentScaledTime; 41 | Realtime = realtime; 42 | } 43 | } 44 | 45 | internal static Info GetFixedInfo() => new (FixedDeltaTime, UnscaledFixedDeltaTime, ScaledFixedTime, Realtime); 46 | 47 | internal static Info GetInfo() => new (DeltaTime, UnscaledDeltaTime, ScaledTime, Realtime); 48 | 49 | internal static float DeltaTime 50 | { 51 | get 52 | { 53 | #if OUTSIDE_UNITY 54 | return 0; 55 | #else 56 | return UnityEngine.Time.deltaTime; 57 | #endif 58 | } 59 | } 60 | 61 | internal static float FixedDeltaTime 62 | { 63 | get 64 | { 65 | #if OUTSIDE_UNITY 66 | return 0; 67 | #else 68 | return UnityEngine.Time.fixedDeltaTime; 69 | #endif 70 | } 71 | } 72 | 73 | internal static float ScaledTime 74 | { 75 | get 76 | { 77 | #if OUTSIDE_UNITY 78 | return 0; 79 | #else 80 | return UnityEngine.Time.time; 81 | #endif 82 | } 83 | } 84 | 85 | internal static float ScaledFixedTime 86 | { 87 | get 88 | { 89 | #if OUTSIDE_UNITY 90 | return 0; 91 | #else 92 | return UnityEngine.Time.fixedTime; 93 | #endif 94 | } 95 | } 96 | 97 | internal static float UnscaledDeltaTime 98 | { 99 | get 100 | { 101 | #if OUTSIDE_UNITY 102 | return 0; 103 | #else 104 | return UnityEngine.Time.unscaledDeltaTime; 105 | #endif 106 | } 107 | } 108 | 109 | internal static float UnscaledFixedDeltaTime 110 | { 111 | get 112 | { 113 | #if OUTSIDE_UNITY 114 | return 0; 115 | #else 116 | return UnityEngine.Time.fixedUnscaledDeltaTime; 117 | #endif 118 | } 119 | } 120 | 121 | internal static float Realtime 122 | { 123 | get 124 | { 125 | #if OUTSIDE_UNITY 126 | return 0; 127 | #else 128 | return UnityEngine.Time.realtimeSinceStartup; 129 | #endif 130 | } 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/UnityBridge/TimeProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 49b655e1c20d9234e9163b448a137c6d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/UpdateInGroupAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Arch.System; 3 | 4 | namespace Arch.SystemGroups 5 | { 6 | /// 7 | /// The specified Type must be a SystemGroup. 8 | /// Updating in a group means this system will be automatically updated by the specified ComponentSystemGroup when the group is updated. 9 | /// The system may order itself relative to other systems in the group with UpdateBefore and UpdateAfter. This ordering takes 10 | /// effect when the system group is sorted. 11 | /// 12 | [AttributeUsage(AttributeTargets.Class)] 13 | public class UpdateInGroupAttribute : Attribute 14 | { 15 | /// 16 | /// Specify the or which the tagged system should be added to. The tagged system 17 | /// will be updated as part of this system group's Update() method. 18 | /// 19 | /// The type/ 20 | /// Thrown id the group type is empty. 21 | public UpdateInGroupAttribute(Type groupType) 22 | { 23 | if (groupType == null) 24 | throw new ArgumentNullException(nameof(groupType)); 25 | 26 | GroupType = groupType; 27 | } 28 | 29 | /// 30 | /// Retrieve the type. 31 | /// 32 | public Type GroupType { get; } 33 | } 34 | } -------------------------------------------------------------------------------- /Arch.SystemGroups/UpdateInGroupAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8ccd9e7c2f6308842895d6b7828c79b1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Arch.SystemGroups/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.arch.systemgroups", 3 | "displayName": "Arch.SystemGroups", 4 | "version": "1.0.0", 5 | "unity": "2022.3", 6 | "description": "Aligns Arch systems with Unity updates", 7 | "keywords": [ 8 | "unity", 9 | "unity3d", 10 | "upm", 11 | "utility", 12 | "arch", 13 | "ecs" 14 | ], 15 | "homepage": "https://github.com/mikhail-dcl/Arch.SystemGroups", 16 | "bugs": { 17 | "url": "https://github.com/mikhail-dcl/Arch.SystemGroups/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+ssh://git@github.com/mikhail-dcl/Arch.SystemGroups.git" 22 | }, 23 | "license": "APACHE 2.0", 24 | "author": "mikhail-dcl (https://github.com/mikhail-dcl/)", 25 | "scripts": { 26 | "test": "echo \"Error: no test specified\" && exit 1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Arch.SystemGroups/package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99bdf84c022d31b43982407cf7677325 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /libs/UnityEngine.CoreModule.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhail-dcl/Arch.SystemGroups/50f2f6bf1de7914db7cf627922643c27b929897e/libs/UnityEngine.CoreModule.dll --------------------------------------------------------------------------------