appcore_render/scene_commands.rs
1// =============================================================================
2// #######
3// ### ### F: scene_commands.rs
4// ## ## ## ## P: AppCore-Runtime
5// ## ##
6// C: 2026/10/14 16:59:22 by dnettoRaw
7// ## ## ## ## U: 2026/10/15 10:24:40 by dnettoRaw
8// ########### S: 0.1.0-alpha.1
9// =============================================================================
10
11use crate::{scene_storage::reserved, EntityId, RenderError, SceneWorld, Velocity};
12/// Deferred structural operations, applied only when mutable world ownership is available.
13#[derive(Debug, Clone, Copy)]
14pub enum SceneCommand {
15 /// Migrate to/from the velocity archetype without changing identity.
16 SetVelocity(EntityId, Option<Velocity>),
17 /// Remove one live entity; stale/foreign identities return a controlled error.
18 Despawn(EntityId),
19}
20/// Independent bounded structural queue. Application owns ordering and frame boundaries.
21pub struct SceneCommands {
22 commands: Vec<SceneCommand>,
23 capacity: usize,
24}
25impl SceneCommands {
26 /// Reserve 1..256 operations; no workers, timers or automatic application.
27 pub fn new(capacity: usize) -> Result<Self, RenderError> {
28 if !(1..=256).contains(&capacity) {
29 return Err(RenderError::InvalidLimits);
30 }
31 Ok(Self {
32 commands: reserved(capacity)?,
33 capacity,
34 })
35 }
36 /// Queue before world mutation; overflow leaves existing queued operations intact.
37 pub fn push(&mut self, command: SceneCommand) -> Result<(), RenderError> {
38 if self.commands.len() == self.capacity {
39 return Err(RenderError::BudgetExceeded);
40 }
41 self.commands.push(command);
42 Ok(())
43 }
44 /// Apply in order, returning one outcome per command. This batch is explicitly nontransactional:
45 /// an invalid command does not undo earlier effects or discard subsequent commands.
46 /// Outcome allocation is admitted before any mutation; no user callbacks run inside the batch.
47 pub fn apply(
48 self,
49 world: &mut SceneWorld,
50 ) -> Result<Vec<Result<(), RenderError>>, RenderError> {
51 let mut outcomes = reserved(self.commands.len())?;
52 for command in self.commands {
53 outcomes.push(match command {
54 SceneCommand::SetVelocity(entity, value) => world.set_velocity(entity, value),
55 SceneCommand::Despawn(entity) => world.despawn(entity),
56 });
57 }
58 Ok(outcomes)
59 }
60}