Implemented features: - High-performance Rust extension with Redis persistence - Actor/player management with loadout, position, and state tracking - Banking system with deposit, withdraw, and transfer operations - Physical and virtual garage/locker systems for vehicle and equipment storage - Organization management with member tracking and permissions - Client-side UI with React-like state management - Server-side event-driven architecture with CBA Events - Security: Self-transfer prevention at multiple layers - Logging system with per-module log files - ICOM module for inter-server communication Co-Authored-By: Warp <agent@warp.dev>
Forge Library
This directory contains the core business logic and data layers for the Forge framework, organized into modular, reusable crates that follow clean architecture principles.
Architecture Overview
The library follows a layered architecture pattern, ensuring separation of concerns and maintainability:
graph TD
Extension[Extension Layer<br/>#40;ArmA 3 Interface#41;]
Services[Services Layer<br/>#40;Business Logic#41;]
Repositories[Repositories Layer<br/>#40;Data Persistence#41;]
Models[Models Layer<br/>#40;Data Structures#41;]
Extension --> Services
Services --> Repositories
Repositories --> Models
Modules
Models (lib/models)
Purpose: Defines strict data structures and validation rules for domain entities.
Responsibilities:
- Define entity structures (
Actor,Org) - Implement validation logic
- Handle serialization/deserialization (JSON, Arma)
- Enforce business rules at the data level
Key Features:
- Strong typing with Rust structs
- Built-in validation on creation and updates
- Automatic email generation for actors
- Arma-specific type conversions
Documentation: models/README.md
Repositories (lib/repositories)
Purpose: Manages data persistence and retrieval using Redis.
Responsibilities:
- Abstract database operations
- Implement CRUD operations
- Handle data serialization to Redis formats
- Manage Redis keys and data structures
Key Features:
- Generic over Redis client implementations
- Hash-based storage for structured data
- Set-based storage for collections (e.g., org members)
- Thread-safe operations (
Send + Sync)
Documentation: repositories/README.md
Services (lib/services)
Purpose: Implements business logic, validation, and orchestration of operations.
Responsibilities:
- Coordinate between repositories
- Enforce business rules
- Handle complex workflows
- Provide high-level APIs for the extension layer
Key Features:
- Generic over repository implementations
- Stateless service design
- Get-or-create patterns for entities
- Comprehensive error handling
Documentation: services/README.md
Shared (lib/shared)
Purpose: Provides common utilities, traits, and helper functions used across all layers.
Responsibilities:
- Define shared traits (
RedisClient) - Provide utility functions
- Common type definitions
- Cross-cutting concerns
Key Features:
RedisClienttrait for repository abstraction- JSON/Redis value parsing utilities
- Arma value conversion helpers
- Reusable helper functions
How It All Works Together
Example: Creating an Actor
Here's how the layers interact when creating a new actor:
-
Extension Layer receives SQF command:
// arma/server/extension/src/actor.rs pub fn create_actor(key: String, data: String) -> String { // Parse JSON and call service ACTOR_SERVICE.create_actor(uid, json_data) } -
Service Layer validates and orchestrates:
// lib/services/src/actor.rs impl<R: ActorRepository> ActorService<R> { pub fn create_actor(&self, uid: String, data: String) -> Result<Actor, String> { // Create actor model (validates data) let actor = Actor::new(uid, data)?; // Persist via repository self.repository.create(&actor)?; Ok(actor) } } -
Repository Layer persists to Redis:
// lib/repositories/src/actor.rs impl<C: RedisClient> ActorRepository for RedisActorRepository<C> { fn create(&self, actor: &Actor) -> Result<(), String> { // Convert actor to Redis hash let fields = actor.to_redis_fields(); // Store in Redis self.client.hash_mset(format!("actor:{}", actor.uid), fields) } } -
Model Layer ensures data integrity:
// lib/models/src/actor.rs impl Actor { pub fn new(uid: String, data: String) -> Result<Self, String> { // Validate all fields Self::validate(&uid, &data)?; // Create actor with validated data Ok(Actor { uid, /* ... */ }) } }
Contributing
We welcome contributions to the Forge library! Follow these guidelines to maintain consistency and quality.
Adding a New Model
See models/README.md - Contributing
Summary:
- Define struct with validation rules
- Implement
newandvalidatemethods - Add serialization traits (
Serialize,Deserialize) - Implement Arma conversions (
FromArma,IntoArma)
Adding a New Repository
See repositories/README.md - Contributing
Summary:
- Define repository trait with
Send + Sync - Implement trait for
RedisXRepository<C: RedisClient> - Use
forge_shared::RedisClientfor operations - Register module in
lib.rs
Adding a New Service
See services/README.md - Contributing
Summary:
- Create service struct generic over repository
- Implement constructor and business logic methods
- Delegate data operations to repository
- Register module in
lib.rs
Best Practices
Separation of Concerns
- Models: Only data structures and validation
- Repositories: Only data persistence logic
- Services: Only business logic and orchestration
- Shared: Only common utilities and traits
Error Handling
- Use
Result<T, String>for all fallible operations - Provide descriptive error messages
- Propagate errors up the stack with
?
Testing
- Models: Test validation rules
- Repositories: Test with mock Redis clients
- Services: Test with mock repositories
- Integration: Test full stack in extension layer
Dependencies
- Models should have minimal dependencies
- Repositories depend on models and shared
- Services depend on repositories and models
- Avoid circular dependencies
Thread Safety
- All repository traits require
Send + Sync - Services are stateless and thread-safe
- Use appropriate synchronization primitives when needed
Module Dependencies
graph TD
Shared[Shared<br/>#40;No Dependencies#41;]
Models[Models<br/>#40;Depends on Shared#41;]
Repositories[Repositories<br/>#40;Depends on Models, Shared#41;]
Services[Services<br/>#40;Depends on Repositories, Models#41;]
Extension[Extension<br/>#40;Depends on Services, Repositories, Models, Shared#41;]
Shared --> Services
Services --> Repositories
Repositories --> Models
Models --> Extension
Development Workflow
- Define Model: Start with data structure and validation
- Create Repository: Implement persistence layer
- Build Service: Add business logic
- Expose in Extension: Create SQF-callable commands
- Test: Verify each layer independently and together