use core::marker::PhantomData;
use crate::abi::{EntityDataFlags, Status, Str};
use crate::ecs::Entity;
pub use crate::item_data::{MAX_VALUE, SetError};
use crate::{Host, Load, Off};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Replication {
#[default]
Server,
Clients,
}
pub struct EntityData<T> {
host: Host,
kind: u32,
value: PhantomData<fn() -> T>,
}
impl<T> Clone for EntityData<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for EntityData<T> {}
impl<T> core::fmt::Debug for EntityData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EntityData")
.field("kind", &self.kind)
.finish_non_exhaustive()
}
}
impl<T> EntityData<T>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
#[must_use]
pub fn get(&self, entity: Entity) -> Option<T> {
let mut buffer = [0u8; MAX_VALUE];
let mut len = 0u32;
let status = (self.host.api().item_data_get)(
self.host.handle(),
self.kind,
entity.0,
0,
buffer.as_mut_ptr(),
u32::try_from(buffer.len()).unwrap_or(u32::MAX),
&raw mut len,
);
if !status.is_ok() {
return None;
}
postcard::from_bytes(buffer.get(..usize::try_from(len).ok()?)?).ok()
}
pub fn set(&self, entity: Entity, value: &T) -> Result<(), SetError> {
let bytes = postcard::to_allocvec(value).map_err(|e| SetError::Encode(e.to_string()))?;
if bytes.is_empty() || bytes.len() > MAX_VALUE {
return Err(SetError::TooLarge(bytes.len()));
}
self.write(entity, &bytes)
}
pub fn remove(&self, entity: Entity) -> Result<(), SetError> {
self.write(entity, &[])
}
fn write(&self, entity: Entity, bytes: &[u8]) -> Result<(), SetError> {
let status = (self.host.api().item_data_set)(
self.host.handle(),
self.kind,
entity.0,
0,
bytes.as_ptr(),
u32::try_from(bytes.len()).unwrap_or(u32::MAX),
);
match status {
Status::OK => Ok(()),
Status::REFUSED | Status::WRONG_THREAD => Err(SetError::NotServer),
Status::MISSING => Err(SetError::NoWorld),
Status::BAD_ARGS => Err(SetError::NoEntity),
other => Err(SetError::Refused(other.0)),
}
}
}
impl Load<'_> {
pub fn entity_data<T>(
&mut self,
id: &str,
replication: Replication,
) -> Result<EntityData<T>, Off>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
let host = self.host();
let flags = match replication {
Replication::Server => EntityDataFlags::NONE,
Replication::Clients => EntityDataFlags::SENT,
};
let mut kind = 0;
let status =
(host.api().entity_data_open)(host.handle(), Str::new(id), flags, &raw mut kind);
crate::load::ok(
status,
EntityData {
host,
kind,
value: PhantomData,
},
|| format!("entity data {id:?}"),
)
}
}