Jacob Schmidt ebfe77a340 feat: implement complete Forge framework with Rust/Redis backend and Arma 3 integration
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>
2026-01-04 12:52:15 -06:00

103 lines
3.5 KiB
Rust

//! Entry point and runtime bootstrap for the Forge Arma server extension.
//!
//! Initializes a global async runtime, the Redis connection pool, and registers
//! all command groups. Provides status/version commands and maintains a shared
//! Arma `Context` for engine interop.
//!
#![allow(future_incompatible)] // Future-incompatible lint is triggered by arma_rs
use arma_rs::{Context, Extension, Group, arma};
use std::sync::{LazyLock, OnceLock, RwLock as StdRwLock};
use tokio::runtime::{Builder, Runtime};
use tokio::sync::RwLock as TokioRwLock;
pub mod actor;
pub mod adapters;
pub mod bank;
pub mod garage;
pub mod helpers;
pub mod icom;
pub mod locker;
mod log;
pub mod org;
pub mod redis;
pub mod v_garage;
pub mod v_locker;
/// Global Arma `Context` captured at initialization and made available to
/// commands that need engine interop. Stored inside an async `RwLock` to
/// allow mutation by the startup task and later reads.
static CONTEXT: LazyLock<TokioRwLock<Option<Context>>> = LazyLock::new(|| TokioRwLock::new(None));
/// Global Redis connection pool, created once and shared by all commands.
/// Initialized asynchronously after `init()` returns so the extension starts
/// quickly without blocking the main thread.
static REDIS_POOL: OnceLock<redis::client::RedisClient> = OnceLock::new();
/// Global multi-threaded Tokio runtime used to execute async operations from
/// command handlers and startup tasks.
static RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to create tokio runtime")
});
#[derive(Clone, Copy, PartialEq)]
/// Connection state for the Redis pool so SQF can gate behavior on readiness.
enum ConnectionState {
Initializing,
Connected,
Failed,
}
static CONNECTION_STATE: LazyLock<StdRwLock<ConnectionState>> =
LazyLock::new(|| StdRwLock::new(ConnectionState::Initializing));
#[arma]
/// Initializes the extension, registers commands/groups, and asynchronously
/// creates the Redis connection pool on the global runtime.
fn init() -> Extension {
let config = redis::config::load();
let ext = Extension::build()
.command("version", get_version)
.command("status", get_status)
.group("redis", redis::group())
.group("actor", actor::group())
.group("bank", bank::group())
.group("garage", garage::group())
.group("icom", icom::group())
.group("locker", locker::group())
.group("org", org::group())
.group(
"owned",
Group::new()
.group("garage", v_garage::group())
.group("locker", v_locker::group()),
)
.finish();
// Spawn initialization tasks for Redis and ICOM
// These run asynchronously and don't block extension startup
// Redis initialization will set the global CONTEXT
RUNTIME.spawn(async move {
redis::initialize(config.redis).await;
});
ext
}
/// Returns current Redis connection state as a string: `initializing`,
/// `connected`, or `failed`. Intended for SQF polling before issuing
/// operations that require Redis.
fn get_status() -> String {
let state = *CONNECTION_STATE.read().unwrap();
match state {
ConnectionState::Initializing => "initializing".into(),
ConnectionState::Connected => "connected".into(),
ConnectionState::Failed => "failed".into(),
}
}
/// Returns the extension version string for diagnostics and tooling.
pub fn get_version() -> String {
format!("forge-server v{}", env!("CARGO_PKG_VERSION"))
}