my_ecs/ecs/
cmd.rs

1use super::{
2    ent::{component::Component, entity::EntityId},
3    entry::Ecs,
4    post::EntMoveStorage,
5    sched::comm::CommandSender,
6    DynResult,
7};
8use crate::{ds::ReadyFuture, util::macros::impl_from_for_enum};
9use std::{fmt, ptr::NonNull, sync::MutexGuard};
10
11pub mod prelude {
12    pub use super::Command;
13}
14
15/// Command to an ECS instance.
16///
17/// Command is one way to modify ECS instance directly such as adding or
18/// removing systems. In the command method, an ECS handle is given and you can
19/// make change to the ECS insance using the handle.
20///
21/// # Example
22///
23/// ```
24/// use my_ecs::prelude::*;
25///
26/// struct MyCommand;
27///
28/// impl Command for MyCommand {
29///     fn command(&mut self, mut ecs: Ecs) -> DynResult<()> {
30///         ecs.add_system(|| { /* ... */}).take()?;
31///         Ok(())
32///     }
33/// }
34/// ```
35pub trait Command: Send + 'static {
36    /// A method accessing the whole ECS instance.
37    ///
38    /// After calling this method, the command will be dropped.
39    #[allow(unused_variables)]
40    fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()>;
41
42    /// Cancellation method which is called when the command cannot be
43    /// executed for some reason.
44    ///
45    /// After calling this method, the command will be dropped.
46    fn cancel(&mut self) {}
47}
48
49impl fmt::Debug for dyn Command {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "dyn Command")
52    }
53}
54
55impl<F> Command for Option<F>
56where
57    F: FnOnce(Ecs) -> DynResult<()> + Send + 'static,
58{
59    fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
60        if let Some(f) = self.take() {
61            f(ecs)
62        } else {
63            Err("command has been taken".into())
64        }
65    }
66}
67
68impl<F> Command for F
69where
70    F: FnMut(Ecs) -> DynResult<()> + Send + 'static,
71{
72    fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
73        self(ecs)
74    }
75}
76
77impl<F> Command for DynResult<F>
78where
79    F: FnOnce(Ecs) -> DynResult<()> + Send + 'static,
80{
81    fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
82        let empty = Err("command has been taken".into());
83        let this = std::mem::replace(self, empty);
84
85        match this {
86            Ok(f) => f(ecs),
87            Err(e) => Err(e),
88        }
89    }
90}
91
92/// Empty command.
93///
94/// This implementation helps clients to use '?' operator in their command
95/// functions.
96impl Command for DynResult<()> {
97    fn command(&mut self, _ecs: Ecs<'_>) -> DynResult<()> {
98        let empty = Err("command has been taken".into());
99        std::mem::replace(self, empty)
100    }
101}
102
103/// Empty command.
104///
105/// This implementation allows clients make commands returning just `()`, called
106/// unit.
107impl Command for () {
108    fn command(&mut self, _ecs: Ecs<'_>) -> DynResult<()> {
109        Ok(())
110    }
111}
112
113#[derive(Debug)]
114pub(crate) enum CommandObject {
115    /// Trait object command.
116    Boxed(Box<dyn Command>),
117
118    /// Ready future containing command.
119    Future(ReadyFuture),
120
121    Raw(RawCommand),
122}
123
124impl_from_for_enum!("outer" = CommandObject; "var" = Boxed; "inner" = Box<dyn Command>);
125impl_from_for_enum!("outer" = CommandObject; "var" = Future; "inner" = ReadyFuture);
126impl_from_for_enum!("outer" = CommandObject; "var" = Raw; "inner" = RawCommand);
127
128impl CommandObject {
129    pub(crate) fn command(self, ecs: Ecs<'_>) -> DynResult<()> {
130        match self {
131            Self::Boxed(mut boxed) => boxed.command(ecs),
132            Self::Future(future) => {
133                // Safety: consume() requires correct `Arg` and `CR` types.
134                // - `Arg` type is `Ecs<'_>`.
135                // - `CR` type is `DynResult<()>`.
136                // We matched the types with `consume_ready_future`.
137                unsafe { future.consume::<Ecs<'_>, DynResult<()>>(ecs) }
138            }
139            Self::Raw(raw) => raw.command(ecs),
140        }
141    }
142
143    pub(crate) fn cancel(self) {
144        match self {
145            Self::Boxed(mut boxed) => boxed.cancel(),
146            Self::Future(_future) => {}
147            Self::Raw(raw) => raw.cancel(),
148        }
149    }
150}
151
152/// Like other commands, RawCommand is also executed only once.
153#[derive(Debug)]
154pub(crate) struct RawCommand {
155    data: NonNull<u8>,
156    command: unsafe fn(NonNull<u8>, Ecs<'_>) -> DynResult<()>,
157    cancel: unsafe fn(NonNull<u8>),
158}
159
160unsafe impl Send for RawCommand {}
161
162impl RawCommand {
163    pub(crate) unsafe fn new<C: Command>(cmd: &C) -> Self {
164        let data = unsafe { NonNull::new_unchecked((cmd as *const _ as *const u8).cast_mut()) };
165
166        unsafe fn command<C: Command>(data: NonNull<u8>, ecs: Ecs<'_>) -> DynResult<()> {
167            let data = unsafe { data.cast::<C>().as_mut() };
168            data.command(ecs)
169        }
170
171        unsafe fn cancel<C: Command>(data: NonNull<u8>) {
172            let data = unsafe { data.cast::<C>().as_mut() };
173            data.cancel();
174        }
175
176        Self {
177            data,
178            command: command::<C>,
179            cancel: cancel::<C>,
180        }
181    }
182
183    fn command(self, ecs: Ecs<'_>) -> DynResult<()> {
184        // Safety: Calling `self.command` is safe because it's guaranteed
185        // by owner called new().
186        unsafe { (self.command)(self.data, ecs) }
187    }
188
189    fn cancel(self) {
190        // Safety: Calling `self.cancel` is safe because it's guaranteed by
191        // owner called new().
192        unsafe { (self.cancel)(self.data) };
193    }
194}
195
196/// A command builder to attach or detach some components to or from an entity.
197///
198/// By attaching or detathcing components, entity move from one entity container
199/// to another arises because the entity doesn't belong to the previous entity
200/// container. If destination entity container doesn't exist at the time, new
201/// entity container is generated first then the entity moves into it.
202///
203/// There are two options about how to handle built command.
204/// * Call [`EntityMoveCommandBuilder::finish`]
205///   - Just build a command then give it to the caller.
206/// * Drop the builder without calling the `finish` method.
207///   - If something has changed, build a command then send it to main worker in
208///     order to handle it.
209pub struct EntityMoveCommandBuilder<'a> {
210    tx_cmd: &'a CommandSender,
211    guard: MutexGuard<'a, EntMoveStorage>,
212    eid: EntityId,
213    len: usize,
214}
215
216impl<'a> EntityMoveCommandBuilder<'a> {
217    pub(crate) const fn new(
218        tx_cmd: &'a CommandSender,
219        guard: MutexGuard<'a, EntMoveStorage>,
220        eid: EntityId,
221    ) -> Self {
222        Self {
223            tx_cmd,
224            guard,
225            eid,
226            len: 0,
227        }
228    }
229
230    /// Finishes to build an entity move command.
231    pub fn finish(mut self) -> impl Command + 'static {
232        assert!(self.len > 0);
233
234        let cmd = self._finish();
235
236        // Drops mutex guard without scheduling command.
237        self.len = 0;
238        drop(self);
239
240        cmd
241    }
242
243    /// Adds component attachment instruction to a command builder.
244    ///
245    /// The command builder will generate a command with the instruction when
246    /// [`EntityMoveCommandBuilder::finish`] is called.
247    pub fn attach<C: Component>(mut self, component: C) -> Self {
248        self.guard.insert_addition(self.eid, component);
249        self.len += 1;
250        self
251    }
252
253    /// Adds component detatchment instruction to a command builder.
254    ///
255    /// The command builder will generate a command with the instruction when
256    /// [`EntityMoveCommandBuilder::finish`] is called.
257    pub fn detach<C: Component>(mut self) -> Self {
258        self.guard.insert_removal(self.eid, C::key());
259        self.len += 1;
260        self
261    }
262
263    fn _finish(&mut self) -> EntityMoveCommand {
264        self.guard.set_command_length(self.len);
265        EntityMoveCommand
266    }
267}
268
269impl Drop for EntityMoveCommandBuilder<'_> {
270    fn drop(&mut self) {
271        if self.len > 0 {
272            let cmd = self._finish();
273            // Boxing a ZST command doesn't allocate memory for it.
274            self.tx_cmd
275                .send_or_cancel(CommandObject::Boxed(Box::new(cmd)));
276        }
277    }
278}
279
280struct EntityMoveCommand;
281
282impl Command for EntityMoveCommand {
283    fn command(&mut self, ecs: Ecs<'_>) -> DynResult<()> {
284        let post = unsafe { ecs.post_ptr().as_ref() };
285        let mut guard = post.lock_entity_move_storage();
286        guard.consume(ecs);
287        Ok(())
288    }
289}