use crate::engine::commands::Command;
use crate::engine::entity::Entity;
use crate::engine::manager::ECSReference;
use crate::engine::types::ComponentID;
use super::error::AgentError;
pub struct AgentHandle {
entity: Entity,
component_ids: Vec<ComponentID>,
}
impl AgentHandle {
pub fn new(entity: Entity, component_ids: Vec<ComponentID>) -> Self {
let mut component_ids = component_ids;
component_ids.sort_unstable();
component_ids.dedup();
Self {
entity,
component_ids,
}
}
#[inline]
pub fn entity(&self) -> Entity {
self.entity
}
#[inline]
pub fn has_component(&self, id: ComponentID) -> bool {
self.component_ids.binary_search(&id).is_ok()
}
pub fn read<T: 'static + Clone>(
&self,
id: ComponentID,
ecs: ECSReference<'_>,
) -> Result<T, AgentHandleError> {
if !self.has_component(id) {
return Err(AgentHandleError::Agent(AgentError::MissingComponent(id)));
}
ecs.read_entity_component::<T>(self.entity, id)
.map_err(AgentHandleError::ECS)
}
pub fn write<T: 'static + Send>(
&self,
id: ComponentID,
value: T,
ecs: ECSReference<'_>,
) -> Result<(), AgentHandleError> {
if !self.has_component(id) {
return Err(AgentHandleError::Agent(AgentError::MissingComponent(id)));
}
ecs.defer(Command::Set {
entity: self.entity,
component_id: id,
value: Box::new(value),
})
.map_err(AgentHandleError::ECS)
}
}
#[derive(Debug)]
pub enum AgentHandleError {
Agent(AgentError),
ECS(crate::engine::error::ECSError),
}
impl std::fmt::Display for AgentHandleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AgentHandleError::Agent(e) => write!(f, "agent error: {e}"),
AgentHandleError::ECS(e) => write!(f, "ecs error: {e}"),
}
}
}
impl std::error::Error for AgentHandleError {}
impl From<AgentError> for AgentHandleError {
fn from(e: AgentError) -> Self {
AgentHandleError::Agent(e)
}
}
impl From<crate::engine::error::ECSError> for AgentHandleError {
fn from(e: crate::engine::error::ECSError) -> Self {
AgentHandleError::ECS(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::commands::Command;
use crate::engine::component::{Bundle, ComponentRegistry};
use crate::engine::entity::Entity;
use crate::engine::entity::EntityShards;
use crate::engine::manager::ECSManager;
use std::sync::{Arc, RwLock};
fn dummy_entity() -> Entity {
Entity::from_raw(0u64)
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Value(u32);
#[test]
fn has_component_returns_correct_values() {
let handle = AgentHandle::new(dummy_entity(), vec![3, 7, 11]);
assert!(handle.has_component(3));
assert!(handle.has_component(7));
assert!(handle.has_component(11));
assert!(!handle.has_component(0));
assert!(!handle.has_component(5));
}
#[test]
fn missing_component_detected() {
let handle = AgentHandle::new(dummy_entity(), vec![0, 1]);
assert!(!handle.has_component(99));
assert!(handle.has_component(0));
assert!(handle.has_component(1));
}
#[test]
fn entity_accessor_returns_correct_entity() {
let e = Entity::from_raw(42u64);
let handle = AgentHandle::new(e, vec![]);
assert_eq!(handle.entity().to_raw(), 42u64);
}
#[test]
fn write_overwrites_existing_component_with_set_command() {
let registry = Arc::new(RwLock::new(ComponentRegistry::new()));
let value_id = {
let mut registry = registry.write().unwrap();
let value_id = registry.register::<Value>().unwrap();
registry.freeze();
value_id
};
let ecs = ECSManager::with_registry(EntityShards::new(1).unwrap(), registry);
let world = ecs.world_ref();
let mut bundle = Bundle::new();
bundle.insert(value_id, Value(1));
world.defer(Command::Spawn { bundle }).unwrap();
let entity = ecs.apply_deferred_commands().unwrap().spawned[0].entity;
let handle = AgentHandle::new(entity, vec![value_id]);
handle.write(value_id, Value(7), world).unwrap();
ecs.apply_deferred_commands().unwrap();
assert_eq!(handle.read::<Value>(value_id, world).unwrap(), Value(7));
}
}