Skip to content

okandemirel/Strada.Core

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

138 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Strada Framework

A high-performance Unity framework unifying Patterns architecture with ECS simulation

Language: English | 日本語 | 简体中文 | 繁體中文 | 한국어

Tests Unity .NET

Strada combines enterprise-grade dependency injection with performance-critical ECS, wrapped in a clean modular architecture. Build UI with familiar patterns while using ECS for high-performance simulation—without choosing between paradigms.


Table of Contents


Features

Dependency Injection (docs)

  • Container: Expression tree compiled factories (1.56x manual new() overhead)
  • Lifetimes: Singleton, Transient, Scoped with thread-safe initialization
  • Disposal: LIFO disposal order (dependents disposed before dependencies)
  • Auto-Binding: Attribute-based service registration with [AutoRegister], [AutoRegisterSingleton], etc.
  • Circular Detection: Build-time cycle detection prevents runtime errors
  • Zero-alloc Resolution: No GC allocation for singleton/scoped paths
  • Thread-Safe: Volatile reads, ConcurrentDictionary, and lock-based disposal safety

Entity Component System (docs)

  • SparseSet Storage: Cache-friendly component iteration (6-28ns per entity)
  • Query System: ForEach<T1...T16>() - up to 8 hand-written, 9-16 source-generated
  • Safety: EntityCommandBuffer for safe structural changes during iteration
  • Parallel Jobs: Burst-compiled jobs with 17x speedup over sequential
  • Entity Recycling: Automatic index reuse with version tracking
  • Source Generation: Compile-time query generation for 9-16 components

Messaging (docs)

  • MessageBus: Unified command/query/event bus with array-indexed dispatch (4ns/dispatch)
  • Pooled Commands: Execute ICommand objects with automatic pool return
  • Zero-alloc Publish: Struct-based messages, no boxing
  • Exception Isolation: Handler failures don't interrupt other subscribers

Patterns-ECS Sync (docs)

  • Event-Driven Integration: ECS systems publish ComponentChanged events, Patterns controllers subscribe
  • EntityMediator: Binds ECS entities to UI views with auto-sync and MessageBus integration
  • Bidirectional Flow: Controllers send commands to ECS via MessageBus, receive events back

Reactive Bindings (docs)

  • ReactiveProperty: Observable values with change notification
  • ReactiveCollection: Observable lists with add/remove/clear events
  • ComputedProperty: Derived values with automatic dependency tracking

Modular Architecture (docs)

  • ModuleConfig: ScriptableObject-based module configuration
  • Inspector Systems: Configure ECS systems via drag-and-drop
  • IModuleBuilder: VContainer-like fluent API for DI registration
  • System Discovery: Auto-find systems with [StradaSystem] attribute
  • Priority Ordering: Control module initialization order

Utilities

  • ObjectPool: Generic pooling with lifecycle hooks (Spawn/Despawn)
  • StateMachine: Type-safe FSM with conditional transitions
  • TimerService: Managed timers with pause/resume support

Installation

Add to your Unity project's Packages/manifest.json:

{
  "dependencies": {
    "com.strada.core": "file:../Packages/com.strada.core"
  }
}

Or copy the Packages/com.strada.core folder directly into your project.

Requirements:

  • Unity 6000.0+ (Unity 6)
  • .NET Standard 2.1

Quick Start

Dependency Injection

using Strada.Core.DI;
using Strada.Core.DI.Attributes;

// Option 1: Manual registration
var builder = new ContainerBuilder();
builder.Register<IPlayerService, PlayerService>(Lifetime.Singleton);
builder.Register<IInputService, InputService>(Lifetime.Singleton);
using var container = builder.Build();

// Option 2: Auto-binding with attributes
[AutoRegisterSingleton(As = typeof(IPlayerService))]
public class PlayerService : IPlayerService { }

[AutoRegisterTransient]
public class EnemyController { }

// Auto-register all attributed types
var builder = new ContainerBuilder();
builder.RegisterAutoBindings();  // Scans for [AutoRegister*] attributes
using var container = builder.Build();

ECS System

using Strada.Core.ECS;
using Strada.Core.ECS.Query;

// Define components (must be unmanaged structs)
public struct Position : IComponent { public float X, Y, Z; }
public struct Velocity : IComponent { public float X, Y, Z; }
public struct Health : IComponent { public int Current, Max; }
public struct Damage : IComponent { public int Value; }

// Query up to 8 components (hand-written, optimal performance)
entityManager.ForEach<Position, Velocity, Health, Damage>(
    (int entity, ref Position pos, ref Velocity vel, ref Health hp, ref Damage dmg) =>
    {
        pos.X += vel.X * deltaTime;
    });

// Query 9-16 components (source-generated)
entityManager.ForEach<T1, T2, T3, T4, T5, T6, T7, T8, T9>(...);

// Or use SystemBase for cleaner code
public class MovementSystem : SystemBase<Position, Velocity>
{
    protected override void OnUpdateEntity(int entity, ref Position pos, ref Velocity vel, float dt)
    {
        pos.X += vel.X * dt;
        pos.Y += vel.Y * dt;
        pos.Z += vel.Z * dt;
    }
}

Messaging

using Strada.Core.Communication;

// Define messages as structs
public struct PlayerDamaged { public int EntityId; public int Damage; }
public struct SpawnEnemy { public float X, Y; }

// Setup bus
var bus = new MessageBus();

// Subscribe to events
bus.Subscribe<PlayerDamaged>(e => Debug.Log($"Player took {e.Damage} damage"));

// Publish events (zero allocation)
bus.Publish(new PlayerDamaged { EntityId = 1, Damage = 10 });

// Register command handlers
bus.RegisterCommandHandler<SpawnEnemy>(cmd => SpawnEnemyAt(cmd.X, cmd.Y));
bus.Send(new SpawnEnemy { X = 10, Y = 20 });

Reactive Properties

using Strada.Core.Sync;

// Create reactive property
var health = new ReactiveProperty<int>(100);

// Subscribe to changes
health.Subscribe(value => healthBar.SetValue(value));

// Changes automatically notify subscribers
health.Value = 75; // healthBar updates automatically

Performance

Honest benchmarks measured on Apple Silicon (Unity 6, Mono):

DI Container

Operation Time Notes
Simple Transient 0.11μs Single class, no dependencies
4-Level Deep Chain 0.27μs A→B→C→D dependency chain
Wide Service (5 deps) 0.42μs Class with 5 injected dependencies
Singleton Lookup 61ns Already-created singleton
Scoped Lookup 21ns Within existing scope
Container Build (100 types) 0.05ms ~0.5μs per registration
vs Manual new() 1.56x Competitive with best Unity DI

ECS

Operation Time Notes
Entity Creation 54ns Bare entity
Entity + 3 Components 374ns Full entity setup
Single Component Query 6.6ns/entity 100k entities
Two Component Query 18ns/entity 100k entities
Three Component Query 28ns/entity 100k entities
GetComponent 67ns Random access
Simulation (100k, 10 frames) 1.62ms/frame Position += Velocity
Parallel Job Speedup 17x vs sequential ForEach

Memory

Metric Value
Memory per Entity (2 components) 56 bytes
GC Allocation (Singleton resolve) 0 bytes
GC Allocation (Scoped resolve) 0 bytes

Comparison

Framework Resolution Speed vs Manual
Strada 0.11-0.27μs 1.56x
VContainer ~0.2-0.3μs ~2x
Reflex ~0.5-1.0μs ~3-5x
Zenject ~2-5μs ~20-50x

Documentation

Document Description
Modules Modular architecture, ModuleConfig, Inspector-configurable systems
DI Container Dependency injection, lifetimes, scopes
ECS System Entities, components, queries, systems
Messaging MessageBus, commands, events, queries
Sync Reactive properties, bindings, EntityMediator
Pooling Object pools, lifecycle hooks
StateMachine FSM with transitions
TimerService Managed timers with pause/resume
Debugging Troubleshooting, common issues, debugging tools
Benchmarks Full performance data

Architecture

System Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                          STRADA FRAMEWORK                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌─────────────────────────┐     ┌─────────────────────────┐          │
│   │    PATTERNS LAYER       │     │      ECS LAYER          │          │
│   │                         │     │                         │          │
│   │  ┌─────────────────┐    │     │  ┌─────────────────┐    │          │
│   │  │     Views       │    │     │  │    Systems      │    │          │
│   │  │ (MonoBehaviour) │    │     │  │  (SystemBase)   │    │          │
│   │  └────────┬────────┘    │     │  └────────┬────────┘    │          │
│   │           │             │     │           │             │          │
│   │  ┌────────▼────────┐    │     │  ┌────────▼────────┐    │          │
│   │  │   Controllers   │    │     │  │    Entities     │    │          │
│   │  │  (Controller)   │    │     │  │  (EntityManager)│    │          │
│   │  └────────┬────────┘    │     │  └────────┬────────┘    │          │
│   │           │             │     │           │             │          │
│   │  ┌────────▼────────┐    │     │  ┌────────▼────────┐    │          │
│   │  │    Services     │    │     │  │   Components    │    │          │
│   │  │    (Service)    │    │     │  │  (IComponent)   │    │          │
│   │  └────────┬────────┘    │     │  └─────────────────┘    │          │
│   │           │             │     │                         │          │
│   │  ┌────────▼────────┐    │     │                         │          │
│   │  │     Models      │    │     │                         │          │
│   │  │    (Model)      │    │     │                         │          │
│   │  └─────────────────┘    │     │                         │          │
│   └────────────┬────────────┘     └────────────┬────────────┘          │
│                │                               │                        │
│                └───────────┬───────────────────┘                        │
│                            │                                            │
│                ┌───────────▼───────────┐                                │
│                │      MessageBus       │                                │
│                │  (Events/Commands/    │                                │
│                │       Queries)        │                                │
│                └───────────┬───────────┘                                │
│                            │                                            │
│    ┌───────────────────────┼───────────────────────┐                    │
│    │                       │                       │                    │
│    ▼                       ▼                       ▼                    │
│ ┌──────────────┐   ┌──────────────┐   ┌──────────────────────┐         │
│ │     DI       │   │   Reactive   │   │   Sync/Mediator      │         │
│ │  Container   │   │  Properties  │   │      Registry        │         │
│ │ (Container)  │   │(ReactiveProperty) │  (EntityMediator)    │         │
│ └──────────────┘   └──────────────┘   └──────────────────────┘         │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Data Flow

┌──────────────┐   Commands    ┌──────────────┐   Component     ┌──────────┐
│  Controller  │──────────────▶│  MessageBus  │───Updates──────▶│   ECS    │
│              │               │              │                 │  System  │
└──────────────┘               └──────────────┘                 └──────────┘
       ▲                              │                               │
       │                              │                               │
       │         ComponentChanged     │      Publish Events           │
       └──────────────────────────────┴───────────────────────────────┘

Folder Structure

Packages/com.strada.core/
├── Runtime/
│   ├── DI/                    # Dependency Injection
│   │   ├── ContainerBuilder.cs
│   │   ├── Container.cs
│   │   ├── ContainerScope.cs
│   │   ├── Lifetime.cs
│   │   ├── Attributes/        # Auto-binding attributes
│   │   │   └── AutoRegisterAttribute.cs
│   │   └── AutoBinding/       # Runtime scanner
│   │       └── RuntimeAutoBindingScanner.cs
│   ├── ECS/                   # Entity Component System
│   │   ├── Core/EntityManager.cs
│   │   ├── Storage/SparseSet.cs
│   │   ├── Query/QueryBuilder.cs
│   │   ├── Systems/SystemBase.cs
│   │   └── Jobs/ParallelComponentJob.cs
│   ├── Communication/         # Unified Messaging
│   │   └── MessageBus.cs
│   ├── Commands/              # Command Pattern
│   │   ├── ICommand.cs
│   │   ├── CommandPool.cs
│   │   └── CommandSequencer.cs
│   ├── Sync/                  # Patterns-ECS Integration
│   │   ├── ReactiveProperty.cs
│   │   ├── ComputedProperty.cs
│   │   ├── EntityMediator.cs
│   │   └── SyncEvents.cs
│   ├── Modules/               # Modular Architecture
│   │   ├── ModuleConfig.cs        # Base module ScriptableObject
│   │   ├── IModuleBuilder.cs      # Fluent registration API
│   │   ├── ModuleBuilder.cs       # Builder implementation
│   │   ├── SystemRunner.cs        # Config-driven system execution
│   │   ├── SystemEntry.cs         # System configuration
│   │   ├── ServiceEntry.cs        # Service configuration
│   │   └── SystemAttributes.cs    # [StradaSystem] attribute
│   ├── Bootstrap/             # Application Bootstrap
│   │   ├── GameBootstrapper.cs    # Main entry point
│   │   └── GameBootstrapperConfig.cs  # Central orchestrator
│   ├── Pooling/               # Object Pooling
│   │   └── ObjectPool.cs
│   └── StateMachine/          # FSM
│       └── StateMachine.cs
├── Documentation~/            # Detailed Documentation
│   ├── Modules.md             # Modular architecture guide
│   ├── DI.md                  # Dependency injection guide
│   ├── ECS.md                 # Entity Component System guide
│   ├── Messaging.md           # MessageBus messaging guide
│   ├── Sync.md                # Reactive bindings guide
│   ├── Pooling.md             # Object pooling guide
│   ├── StateMachine.md        # FSM guide
│   └── Benchmarks.md          # Performance benchmarks
├── SourceGenerationDI~/       # DI Roslyn Source Generators
│   └── StradaDISourceGenerator.cs  # DI auto-binding generation
├── SourceGenerationECS~/      # ECS Roslyn Source Generators
│   ├── StradaFactoryGenerator.cs   # Factory generation
│   └── EntityQueryGenerator.cs     # Query T9-T16 generation
├── Editor/                    # Editor Tools
└── Tests/                     # Test Suite
    ├── Runtime/               # Functional Tests (324)
    └── Performance/           # Benchmarks (93)

API Reference

ContainerBuilder

// Register interface → implementation
builder.Register<IService, ServiceImpl>(Lifetime.Singleton);

// Register concrete type
builder.Register<MyService>(Lifetime.Transient);

// Register factory
builder.RegisterFactory<IService>(c => new ServiceImpl(c.Resolve<IDep>()));

// Register instance
builder.RegisterInstance<IConfig>(configInstance);

// Build container
IContainer container = builder.Build();

IContainer

T Resolve<T>() where T : class;
object Resolve(Type type);
bool TryResolve<T>(out T instance) where T : class;
bool IsRegistered<T>() where T : class;
IContainerScope CreateScope();

EntityManager

Entity CreateEntity();
void DestroyEntity(Entity entity);
bool Exists(Entity entity);

void AddComponent<T>(Entity entity, T component) where T : unmanaged, IComponent;
void RemoveComponent<T>(Entity entity) where T : unmanaged, IComponent;
bool HasComponent<T>(Entity entity) where T : unmanaged, IComponent;
T GetComponent<T>(Entity entity) where T : unmanaged, IComponent;
void SetComponent<T>(Entity entity, T component) where T : unmanaged, IComponent;

MessageBus

// Events (pub/sub)
void Subscribe<TEvent>(Action<TEvent> handler) where TEvent : struct;
void Unsubscribe<TEvent>(Action<TEvent> handler) where TEvent : struct;
void Publish<TEvent>(TEvent evt) where TEvent : struct;

// Struct Commands (request/response)
void RegisterCommandHandler<TCommand>(Action<TCommand> handler) where TCommand : struct;
void Send<TCommand>(TCommand command) where TCommand : struct;

// Object Commands (pooled, async)
void Execute(ICommand command);          // Auto-returns pooled commands
void ExecuteAsync(IAsyncCommand command, Action onComplete = null);

// Queries (request/response with return)
void RegisterQueryHandler<TQuery, TResult>(Func<TQuery, TResult> handler);
TResult Query<TQuery, TResult>(TQuery query) where TQuery : struct, IQuery<TResult>;

ReactiveProperty

var prop = new ReactiveProperty<int>(initialValue);

prop.Value;                          // Get current value
prop.Value = newValue;               // Set and notify
prop.SetWithoutNotify(value);        // Set without notification
prop.Subscribe(handler);             // Subscribe to changes
prop.SubscribeAndInvoke(handler);    // Subscribe and call immediately
prop.Unsubscribe(handler);           // Remove subscription

ObjectPool

var pool = new ObjectPool<Enemy>(
    factory: () => new Enemy(),
    onSpawn: e => e.Reset(),
    onDespawn: e => e.Cleanup(),
    initialSize: 10,
    maxSize: 100
);

Enemy enemy = pool.Spawn();
pool.Despawn(enemy);
pool.Prewarm(20);
pool.Clear();

StateMachine

var fsm = new StateMachine<IState>();

fsm.AddState(new IdleState());
fsm.AddState(new WalkState());
fsm.AddState(new AttackState());

fsm.AddTransition<IdleState, WalkState>(() => input.IsMoving);
fsm.AddTransition<WalkState, IdleState>(() => !input.IsMoving);
fsm.AddAnyTransition<AttackState>(() => input.IsAttacking);

fsm.Start<IdleState>();
fsm.Update(deltaTime);

Testing

# Run all tests (Unity must be closed)
./run_tests.sh

# Run functional tests only
UNITY_PATH="/path/to/Unity" PROJECT_PATH="/path/to/project"
"$UNITY_PATH" -batchmode -projectPath "$PROJECT_PATH" \
  -runTests -testPlatform playmode \
  -testCategory "!Performance"

# Run benchmarks only
"$UNITY_PATH" -batchmode -projectPath "$PROJECT_PATH" \
  -runTests -testPlatform playmode \
  -testCategory "Performance"

Test Coverage:

  • 330 functional tests (Hardened DI & ECS edge cases)
  • 94 performance benchmarks (Added realistic simulation scenarios)
  • All 424 tests passing

License

Proprietary - All rights reserved


Contributing

This is a private framework. For bug reports or feature requests, contact the maintainer.


Built for Unity 6 with performance and clean architecture in mind.

About

High-performance Unity 6 framework unifying DI (1.56x manual new()), SparseSet ECS (6ns/entity), MessageBus (4ns/dispatch), ReactiveProperty bindings, and modular architecture. Zero-alloc, Burst-ready, source-generated.

Resources

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

 
 
 

Contributors

Languages