forge/lib/models/src/garage.rs
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

156 lines
4.2 KiB
Rust

use arma_rs::{FromArma, IntoArma};
use forge_shared::GarageValidationError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Garage {
pub vehicles: HashMap<String, Vehicle>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Vehicle {
pub plate: String,
pub classname: String,
pub fuel: f64,
pub damage: f64,
pub hit_points: HitPoints,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HitPoints {
pub names: Vec<String>,
pub selections: Vec<String>,
pub values: Vec<f64>,
}
impl HitPoints {
pub fn new() -> Self {
Self {
names: Vec::new(),
selections: Vec::new(),
values: Vec::new(),
}
}
pub fn from_json_str(json_str: &str) -> Result<Self, String> {
let hit_points: HitPoints = serde_json::from_str(json_str)
.map_err(|e| format!("Failed to parse hit_points JSON: {}", e))?;
let names_len = hit_points.names.len();
let selections_len = hit_points.selections.len();
let values_len = hit_points.values.len();
if names_len != selections_len || names_len != values_len {
return Err(format!(
"Hitpoint array length mismatch: names={}, selections={}, values={}",
names_len, selections_len, values_len
));
}
Ok(hit_points)
}
}
impl Vehicle {
pub fn new<S: Into<String>>(
plate: S,
classname: S,
fuel: f64,
damage: f64,
hit_points: HitPoints,
) -> Result<Self, GarageValidationError> {
let vehicle = Self {
plate: plate.into(),
classname: classname.into(),
fuel,
damage,
hit_points,
};
vehicle.validate()?;
Ok(vehicle)
}
pub fn validate(&self) -> Result<(), GarageValidationError> {
if self.classname.trim().is_empty() {
return Err(GarageValidationError::ClassnameEmpty);
}
if self.fuel < 0.0 || self.fuel > 1.0 {
return Err(GarageValidationError::FuelInvalid);
}
if self.damage < 0.0 || self.damage > 1.0 {
return Err(GarageValidationError::DamageInvalid);
}
let names_len = self.hit_points.names.len();
let selections_len = self.hit_points.selections.len();
let values_len = self.hit_points.values.len();
if names_len != selections_len || names_len != values_len {
return Err(GarageValidationError::HitpointArrayLengthMismatch);
}
for (i, value) in self.hit_points.values.iter().enumerate() {
if *value < 0.0 || *value > 1.0 {
return Err(GarageValidationError::HitpointValueInvalid(i));
}
}
Ok(())
}
}
impl Garage {
pub fn new() -> Result<Self, GarageValidationError> {
let garage = Self {
vehicles: HashMap::new(),
};
garage.validate()?;
Ok(garage)
}
pub fn validate(&self) -> Result<(), GarageValidationError> {
for vehicle in self.vehicles.values() {
vehicle.validate()?;
}
Ok(())
}
pub fn add_vehicle(&mut self, vehicle: Vehicle) -> Result<(), GarageValidationError> {
vehicle.validate()?;
self.vehicles.insert(vehicle.plate.clone(), vehicle);
Ok(())
}
pub fn remove_vehicle(&mut self, plate: &str) -> Option<Vehicle> {
self.vehicles.remove(plate)
}
pub fn get_vehicle(&self, plate: &str) -> Option<&Vehicle> {
self.vehicles.get(plate)
}
pub fn get_vehicle_mut(&mut self, plate: &str) -> Option<&mut Vehicle> {
self.vehicles.get_mut(plate)
}
}
impl FromArma for Vehicle {
fn from_arma(s: String) -> Result<Self, arma_rs::FromArmaError> {
serde_json::from_str(&s)
.map_err(|e| arma_rs::FromArmaError::InvalidPrimitive(format!("Invalid JSON: {}", e)))
}
}
impl IntoArma for Vehicle {
fn to_arma(&self) -> arma_rs::Value {
let json_str = serde_json::to_string(self).unwrap_or_default();
arma_rs::Value::String(json_str)
}
}