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}