khora_control/
registry.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Agent registry for automatic registration and ordered iteration.
16
17use khora_core::agent::Agent;
18use khora_core::control::gorna::AgentId;
19use std::sync::{Arc, Mutex};
20
21/// Entry in the agent registry containing the agent and its priority.
22struct AgentEntry {
23    agent: Arc<Mutex<dyn Agent>>,
24    priority: f32,
25}
26
27/// Registry that manages all registered agents with automatic priority ordering.
28///
29/// Agents are automatically sorted by priority (highest first) and can be
30/// iterated in execution order.
31pub struct AgentRegistry {
32    entries: Vec<AgentEntry>,
33}
34
35impl AgentRegistry {
36    /// Creates a new empty registry.
37    pub fn new() -> Self {
38        Self {
39            entries: Vec::new(),
40        }
41    }
42
43    /// Registers an agent with the given priority.
44    ///
45    /// Higher priority values mean the agent is updated first.
46    pub fn register(&mut self, agent: Arc<Mutex<dyn Agent>>, priority: f32) {
47        let id = agent.lock().map(|a| a.id()).unwrap_or(AgentId::Asset);
48        log::info!(
49            "AgentRegistry: Registered {:?} (priority={:.2})",
50            id,
51            priority
52        );
53
54        self.entries.push(AgentEntry { agent, priority });
55        self.entries.sort_by(|a, b| {
56            b.priority
57                .partial_cmp(&a.priority)
58                .unwrap_or(std::cmp::Ordering::Equal)
59        });
60    }
61
62    /// Returns the number of registered agents.
63    pub fn len(&self) -> usize {
64        self.entries.len()
65    }
66
67    /// Returns true if no agents are registered.
68    pub fn is_empty(&self) -> bool {
69        self.entries.is_empty()
70    }
71
72    /// Returns an iterator over all agents in priority order (highest first).
73    pub fn iter(&self) -> impl Iterator<Item = &Arc<Mutex<dyn Agent>>> {
74        self.entries.iter().map(|e| &e.agent)
75    }
76
77    /// Updates all agents in priority order.
78    pub fn update_all(&self, context: &mut khora_core::EngineContext<'_>) {
79        for entry in &self.entries {
80            if let Ok(mut agent) = entry.agent.lock() {
81                agent.update(context);
82            }
83        }
84    }
85
86    /// Executes all agents in priority order.
87    ///
88    /// Called after [`update_all`](Self::update_all) each frame. Each agent
89    /// performs its primary work (e.g., the `RenderAgent` renders,
90    /// the `PhysicsAgent` simulates).
91    pub fn execute_all(&self) {
92        for entry in &self.entries {
93            if let Ok(mut agent) = entry.agent.lock() {
94                agent.execute();
95            }
96        }
97    }
98
99    /// Returns the agent with the given ID, if registered.
100    pub fn get_by_id(&self, id: AgentId) -> Option<Arc<Mutex<dyn Agent>>> {
101        for entry in &self.entries {
102            if let Ok(agent) = entry.agent.lock() {
103                if agent.id() == id {
104                    return Some(entry.agent.clone());
105                }
106            }
107        }
108        None
109    }
110}
111
112impl Default for AgentRegistry {
113    fn default() -> Self {
114        Self::new()
115    }
116}