Skip to main content

arkhe_kernel/state/
context.rs

1//! `ActionContext` — read-only Instance view passed to `Action::compute`.
2//!
3//! Domain authors receive this context, observe Instance state through
4//! the limited accessor surface, and return a `Vec<Op>` describing their
5//! intended effects. Mutation is forbidden — the kernel translates Ops
6//! into `StepStage` deltas, which `apply_stage` later commits.
7
8use super::instance::Instance;
9use crate::abi::{EntityId, InstanceId, Tick};
10
11/// Read-only context passed to [`ActionCompute::compute`](super::traits::ActionCompute::compute).
12/// Carries the originating actor, current tick, and a borrowed view
13/// of the instance for limited introspection.
14pub struct ActionContext<'a> {
15    /// Optional originating entity (e.g. the player who submitted the
16    /// action). `None` for system-injected actions.
17    pub actor: Option<EntityId>,
18    /// Tick at which `step()` is processing this action.
19    pub now: Tick,
20    /// `InstanceId` of the instance the action runs against.
21    pub instance_id: InstanceId,
22    pub(crate) instance: &'a Instance,
23}
24
25impl<'a> ActionContext<'a> {
26    /// Construct an `ActionContext` bound to an Instance reference.
27    /// `pub(crate)` because only the kernel scheduler dispatches actions.
28    pub(crate) fn new(
29        actor: Option<EntityId>,
30        now: Tick,
31        instance_id: InstanceId,
32        instance: &'a Instance,
33    ) -> Self {
34        Self {
35            actor,
36            now,
37            instance_id,
38            instance,
39        }
40    }
41
42    /// Number of entities currently registered in the instance.
43    #[inline]
44    pub fn entities_len(&self) -> usize {
45        self.instance.entities_len()
46    }
47
48    /// Total component count across all entities.
49    #[inline]
50    pub fn components_len(&self) -> usize {
51        self.instance.components_len()
52    }
53
54    /// Logical local tick of the instance.
55    #[inline]
56    pub fn local_tick(&self) -> u64 {
57        self.instance.local_tick()
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::state::InstanceConfig;
65
66    #[test]
67    fn context_accessor_matches_instance() {
68        let inst = Instance::new(InstanceId::new(7).unwrap(), InstanceConfig::default());
69        let ctx = ActionContext {
70            actor: Some(EntityId::new(1).unwrap()),
71            now: Tick(0),
72            instance_id: InstanceId::new(7).unwrap(),
73            instance: &inst,
74        };
75        assert_eq!(ctx.entities_len(), 0);
76        assert_eq!(ctx.components_len(), 0);
77        assert_eq!(ctx.local_tick(), 0);
78        assert_eq!(ctx.now, Tick(0));
79        assert_eq!(ctx.instance_id, InstanceId::new(7).unwrap());
80        assert_eq!(ctx.actor, Some(EntityId::new(1).unwrap()));
81    }
82}