khora_control/plugin.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//! Engine plugins — extensible hooks that inject into the frame pipeline.
16
17use khora_core::agent::ExecutionPhase;
18use khora_data::ecs::World;
19use std::collections::HashMap;
20
21/// Boxed plugin hook executed against the world during a specific
22/// `ExecutionPhase`.
23type PluginHook = Box<dyn Fn(&mut World) + Send>;
24
25/// A plugin that injects callbacks into the frame pipeline.
26pub struct EnginePlugin {
27 name: String,
28 hooks: HashMap<ExecutionPhase, PluginHook>,
29}
30
31impl EnginePlugin {
32 /// Creates a new plugin with the given name.
33 pub fn new(name: &str) -> Self {
34 Self {
35 name: name.to_string(),
36 hooks: HashMap::new(),
37 }
38 }
39
40 /// Registers a callback for a specific execution phase.
41 pub fn on_phase(&mut self, phase: ExecutionPhase, f: impl Fn(&mut World) + Send + 'static) {
42 self.hooks.insert(phase, Box::new(f));
43 }
44
45 /// Executes the hook for a phase, if one is registered.
46 pub fn execute(&mut self, phase: ExecutionPhase, world: &mut World) {
47 if let Some(f) = self.hooks.get(&phase) {
48 f(world);
49 }
50 }
51
52 /// Returns true if this plugin has a hook for the given phase.
53 pub fn wants_phase(&self, phase: ExecutionPhase) -> bool {
54 self.hooks.contains_key(&phase)
55 }
56
57 /// Returns the plugin name.
58 pub fn name(&self) -> &str {
59 &self.name
60 }
61}