use super::{
ent::{component::Component, entity::EntityId},
entry::Ecs,
post::EntMoveStorage,
sched::comm::CommandSender,
DynResult,
};
use my_utils::ds::ReadyFuture;
use std::{
fmt::{self, Debug},
ptr::NonNull,
sync::MutexGuard,
};
pub mod prelude {
pub use super::Command;
}
pub trait Command: Send + 'static {
#[allow(unused_variables)]
fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()>;
fn cancel(&mut self) {}
}
impl fmt::Debug for dyn Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "dyn Command")
}
}
impl<F> Command for Option<F>
where
F: FnOnce(Ecs) -> DynResult<()> + Send + 'static,
{
fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
if let Some(f) = self.take() {
f(ecs)
} else {
Err("command has been taken".into())
}
}
}
impl<F> Command for F
where
F: FnMut(Ecs) -> DynResult<()> + Send + 'static,
{
fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
self(ecs)
}
}
impl<F> Command for DynResult<F>
where
F: FnOnce(Ecs) -> DynResult<()> + Send + 'static,
{
fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
let empty = Err("command has been taken".into());
let this = std::mem::replace(self, empty);
match this {
Ok(f) => f(ecs),
Err(e) => Err(e),
}
}
}
impl Command for DynResult<()> {
fn command(&mut self, _ecs: Ecs<'_>) -> DynResult<()> {
let empty = Err("command has been taken".into());
std::mem::replace(self, empty)
}
}
impl Command for () {
fn command(&mut self, _ecs: Ecs<'_>) -> DynResult<()> {
Ok(())
}
}
#[derive(Debug)]
pub(crate) enum CommandObject {
Boxed(Box<dyn Command>),
Future(ReadyFuture),
Raw(RawCommand),
}
my_utils::impl_from_for_enum!("outer" = CommandObject; "var" = Boxed; "inner" = Box<dyn Command>);
my_utils::impl_from_for_enum!("outer" = CommandObject; "var" = Future; "inner" = ReadyFuture);
my_utils::impl_from_for_enum!("outer" = CommandObject; "var" = Raw; "inner" = RawCommand);
impl CommandObject {
pub(crate) fn command(self, ecs: Ecs<'_>) -> DynResult<()> {
match self {
Self::Boxed(mut boxed) => boxed.command(ecs),
Self::Future(future) => {
unsafe { future.consume::<Ecs<'_>, DynResult<()>>(ecs) }
}
Self::Raw(raw) => raw.command(ecs),
}
}
pub(crate) fn cancel(self) {
match self {
Self::Boxed(mut boxed) => boxed.cancel(),
Self::Future(_future) => {}
Self::Raw(raw) => raw.cancel(),
}
}
}
pub(crate) struct RawCommand {
data: NonNull<u8>,
command: unsafe fn(NonNull<u8>, Ecs<'_>) -> DynResult<()>,
cancel: unsafe fn(NonNull<u8>),
}
unsafe impl Send for RawCommand {}
impl RawCommand {
pub(crate) unsafe fn new<C: Command>(data: NonNull<C>) -> Self {
unsafe fn command<C: Command>(data: NonNull<u8>, ecs: Ecs<'_>) -> DynResult<()> {
let data = unsafe { data.cast::<C>().as_mut() };
data.command(ecs)
}
unsafe fn cancel<C: Command>(data: NonNull<u8>) {
let data = unsafe { data.cast::<C>().as_mut() };
data.cancel();
}
Self {
data: data.cast(),
command: command::<C>,
cancel: cancel::<C>,
}
}
fn command(self, ecs: Ecs<'_>) -> DynResult<()> {
unsafe { (self.command)(self.data, ecs) }
}
fn cancel(self) {
unsafe { (self.cancel)(self.data) };
}
}
impl Debug for RawCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawCommand")
.field("data", &self.data)
.finish_non_exhaustive()
}
}
pub struct EntityMoveCommandBuilder<'a> {
tx_cmd: &'a CommandSender,
guard: MutexGuard<'a, EntMoveStorage>,
eid: EntityId,
len: usize,
}
impl<'a> EntityMoveCommandBuilder<'a> {
pub(crate) const fn new(
tx_cmd: &'a CommandSender,
guard: MutexGuard<'a, EntMoveStorage>,
eid: EntityId,
) -> Self {
Self {
tx_cmd,
guard,
eid,
len: 0,
}
}
pub fn finish(mut self) -> impl Command {
assert!(self.len > 0);
let cmd = self._finish();
self.len = 0;
drop(self);
cmd
}
pub fn attach<C: Component>(mut self, component: C) -> Self {
self.guard.insert_addition(self.eid, component);
self.len += 1;
self
}
pub fn detach<C: Component>(mut self) -> Self {
self.guard.insert_removal(self.eid, C::key());
self.len += 1;
self
}
fn _finish(&mut self) -> EntityMoveCommand {
self.guard.set_command_length(self.len);
EntityMoveCommand
}
}
impl Drop for EntityMoveCommandBuilder<'_> {
fn drop(&mut self) {
if self.len > 0 {
let cmd = self._finish();
self.tx_cmd
.send_or_cancel(CommandObject::Boxed(Box::new(cmd)));
}
}
}
struct EntityMoveCommand;
impl Command for EntityMoveCommand {
fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
let post = unsafe { ecs.post_ptr().as_ref() };
let mut guard = post.lock_entity_move_storage();
guard.consume(ecs);
Ok(())
}
}