use std::sync::Mutex;
use crate::ecs::asset_id::AssetId;
#[derive(Debug)]
pub struct CrossfadeRequest {
pub target: AssetId,
pub weights: Vec<f32>,
pub duration_secs: f32,
}
#[derive(Debug)]
pub struct SetParamRequest {
pub target: AssetId,
pub name: String,
pub value: f32,
}
#[derive(Debug, Clone)]
pub struct GraphStateReport {
pub state: String,
pub clock_secs: f32,
pub fading_from: Option<String>,
pub fade_progress: Option<f32>,
pub blend_weights: Option<Vec<f32>>,
pub params: Vec<(String, f32)>,
}
pub enum AnimCommand {
Crossfade {
req: CrossfadeRequest,
reply: std::sync::mpsc::SyncSender<Result<(), String>>,
},
SetParam {
req: SetParamRequest,
reply: std::sync::mpsc::SyncSender<Result<(), String>>,
},
QueryState {
target: AssetId,
reply: std::sync::mpsc::SyncSender<Result<GraphStateReport, String>>,
},
}
static QUEUE: Mutex<Vec<AnimCommand>> = Mutex::new(Vec::new());
#[cfg(test)]
pub(crate) static TEST_LOCK: Mutex<()> = Mutex::new(());
pub fn enqueue(cmd: AnimCommand) {
let mut q = match QUEUE.lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
q.push(cmd);
}
pub(crate) fn drain() -> Vec<AnimCommand> {
let mut q = match QUEUE.lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
std::mem::take(&mut *q)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enqueue_drain_round_trip() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let _ = drain();
let (tx, _rx) = std::sync::mpsc::sync_channel(1);
enqueue(AnimCommand::Crossfade {
req: CrossfadeRequest {
target: AssetId::default(),
weights: vec![1.0, 0.0],
duration_secs: 0.5,
},
reply: tx,
});
let cmds = drain();
assert_eq!(cmds.len(), 1);
assert!(drain().is_empty());
}
#[test]
fn a_poisoned_queue_still_enqueues_and_drains() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let _ = drain();
let _ = std::thread::spawn(|| {
let _held = QUEUE.lock().unwrap();
panic!("poison");
})
.join();
assert!(QUEUE.is_poisoned());
let (tx, _rx) = std::sync::mpsc::sync_channel(1);
enqueue(AnimCommand::QueryState {
target: AssetId::default(),
reply: tx,
});
assert_eq!(drain().len(), 1);
QUEUE.clear_poison();
}
}