plugin.old.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi_serial::{Decodable, Encodable};
  19. use std::{
  20. io::Cursor,
  21. sync::{mpsc, Arc, Mutex},
  22. thread,
  23. time::{Duration, Instant},
  24. };
  25. use crate::{
  26. error::Result,
  27. prop::{Property, PropertySubType, PropertyType},
  28. py::PythonPlugin,
  29. res::{ResourceId, ResourceManager},
  30. scene::{MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNodeId, SceneNodeType},
  31. };
  32. pub enum Category {
  33. Null,
  34. }
  35. pub enum SubCategory {
  36. Null,
  37. }
  38. pub struct SemVer {
  39. pub major: u32,
  40. pub minor: u32,
  41. pub patch: u32,
  42. pub pre: String,
  43. pub build: String,
  44. }
  45. pub struct PluginMetadata {
  46. pub name: String,
  47. pub title: String,
  48. pub desc: String,
  49. pub author: String,
  50. pub version: SemVer,
  51. pub cat: Category,
  52. pub subcat: SubCategory,
  53. // icon
  54. // Permissions
  55. // whitelisted nodes + props/methods (use * for all)
  56. // /window/input/*
  57. }
  58. pub enum PluginEvent {
  59. // (signal_data, user_data)
  60. RecvSignal((Vec<u8>, Vec<u8>)),
  61. }
  62. pub type PluginInstancePtr = Arc<Mutex<Box<dyn PluginInstance + Send>>>;
  63. pub trait Plugin {
  64. fn metadata(&self) -> PluginMetadata;
  65. // Spawns a new context and begins running the plugin in that context
  66. fn start(&self) -> Result<PluginInstancePtr>;
  67. }
  68. pub trait PluginInstance {
  69. fn update(&mut self, event: PluginEvent) -> Result<()>;
  70. }
  71. enum SentinelMethodEvent {
  72. ImportPlugin,
  73. StartPlugin(ResourceId),
  74. }
  75. pub struct Sentinel {
  76. scene_graph: SceneGraphPtr,
  77. plugins: ResourceManager<Box<dyn Plugin>>,
  78. insts: ResourceManager<PluginInstancePtr>,
  79. method_recvr: mpsc::Receiver<(SentinelMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
  80. method_sender: mpsc::SyncSender<(SentinelMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
  81. }
  82. impl Sentinel {
  83. pub fn new(scene_graph: SceneGraphPtr) -> Self {
  84. // Create /plugin in scene graph
  85. //
  86. // Methods provided in SceneGraph under /plugin:
  87. //
  88. // * import_plugin(pycode)
  89. let mut sg = scene_graph.lock().unwrap();
  90. let (method_sender, method_recvr) = mpsc::sync_channel(100);
  91. let node = sg.add_node("plugin", SceneNodeType::Plugins);
  92. let sender = method_sender.clone();
  93. let node_id = node.id;
  94. let method_fn = Box::new(move |arg_data, response_fn| {
  95. sender.send((SentinelMethodEvent::ImportPlugin, node_id, arg_data, response_fn));
  96. });
  97. node.add_method("import", vec![("pycode", "", PropertyType::Str)], vec![], method_fn);
  98. sg.link(node_id, SceneGraph::ROOT_ID).unwrap();
  99. drop(sg);
  100. Self {
  101. scene_graph,
  102. plugins: ResourceManager::new(),
  103. insts: ResourceManager::new(),
  104. method_recvr,
  105. method_sender,
  106. }
  107. }
  108. pub fn run(&mut self) {
  109. loop {
  110. // Monitor all running plugins
  111. // Check last update times
  112. // Kill any slowpokes
  113. // Check any SceneGraph method requests
  114. let deadline = Instant::now() + Duration::from_millis(4000);
  115. let Ok((event, node_id, arg_data, response_fn)) =
  116. self.method_recvr.recv_deadline(deadline)
  117. else {
  118. break
  119. };
  120. let res = match event {
  121. SentinelMethodEvent::ImportPlugin => self.import_py_plugin(node_id, arg_data),
  122. SentinelMethodEvent::StartPlugin(rid) => self.start_plugin(rid, node_id, arg_data),
  123. };
  124. response_fn(res);
  125. }
  126. }
  127. fn import_py_plugin(&mut self, node_id: SceneNodeId, arg_data: Vec<u8>) -> Result<Vec<u8>> {
  128. // Load the python code
  129. let mut cur = Cursor::new(&arg_data);
  130. let pycode = String::decode(&mut cur).unwrap();
  131. let plugin = Box::new(PythonPlugin::new(self.scene_graph.clone(), pycode));
  132. self.import_plugin(plugin)?;
  133. // This function doesn't return anything
  134. // Only success or an Err which is already handled elsewhere
  135. Ok(vec![])
  136. }
  137. fn import_plugin(&mut self, plugin: Box<dyn Plugin>) -> Result<()> {
  138. let metadata = plugin.metadata();
  139. let plugin_rid = self.plugins.alloc(plugin);
  140. let mut scene_graph = self.scene_graph.lock().unwrap();
  141. // Create /plugin/foo
  142. let node = scene_graph.add_node(metadata.name.clone(), SceneNodeType::Plugin);
  143. let node_id = node.id;
  144. // name
  145. let mut prop = Property::new("name", PropertyType::Str, PropertySubType::Null);
  146. prop.set_str(0, metadata.name);
  147. node.add_property(prop).unwrap();
  148. // title
  149. let mut prop = Property::new("title", PropertyType::Str, PropertySubType::Null);
  150. prop.set_str(0, metadata.title);
  151. node.add_property(prop).unwrap();
  152. // desc
  153. let mut prop = Property::new("desc", PropertyType::Str, PropertySubType::Null);
  154. prop.set_str(0, metadata.desc);
  155. node.add_property(prop).unwrap();
  156. // author
  157. let mut prop = Property::new("author", PropertyType::Str, PropertySubType::Null);
  158. prop.set_str(0, metadata.author);
  159. node.add_property(prop).unwrap();
  160. // version
  161. let mut prop = Property::new("version", PropertyType::Uint32, PropertySubType::Null);
  162. prop.set_array_len(3);
  163. prop.set_u32(0, metadata.version.major);
  164. prop.set_u32(1, metadata.version.minor);
  165. prop.set_u32(2, metadata.version.patch);
  166. node.add_property(prop).unwrap();
  167. // TODO: add version.pre and patch, and cat/subcat enums
  168. let mut prop = Property::new("insts", PropertyType::Uint32, PropertySubType::ResourceId);
  169. prop.set_ui_text("instance resource IDs", "The currently running instances of this plugin");
  170. prop.set_unbounded();
  171. node.add_property(prop).unwrap();
  172. // Add method start()
  173. let sender = self.method_sender.clone();
  174. let method_fn = Box::new(move |arg_data, response_fn| {
  175. sender.send((
  176. SentinelMethodEvent::StartPlugin(plugin_rid),
  177. node_id,
  178. arg_data,
  179. response_fn,
  180. ));
  181. });
  182. node.add_method("start", vec![], vec![("inst_rid", "", PropertyType::Uint32)], method_fn);
  183. // Link node
  184. let parent_id = scene_graph.lookup_node_id("/plugin").expect("no plugin node attached");
  185. scene_graph.link(node_id, parent_id).unwrap();
  186. Ok(())
  187. }
  188. fn start_plugin(
  189. &mut self,
  190. plugin_rid: ResourceId,
  191. node_id: SceneNodeId,
  192. arg_data: Vec<u8>,
  193. ) -> Result<Vec<u8>> {
  194. let plugin = self.plugins.get(plugin_rid).expect("plugin not found");
  195. // Call init()
  196. // Spawn a new thread, allocate it an ID
  197. // Thread waits for events from the scene_graph and calls update() when they occur.
  198. // See src/net.rs:81 for an example
  199. let inst = plugin.start()?;
  200. let inst2 = inst.clone();
  201. let inst_rid = self.insts.alloc(inst);
  202. let _ = thread::spawn(move || {
  203. inst2.lock().unwrap().update(PluginEvent::RecvSignal((vec![], vec![]))).unwrap();
  204. });
  205. let scene_graph = self.scene_graph.lock().unwrap();
  206. let node = scene_graph.get_node(node_id).expect("node not found");
  207. let prop = node.get_property("insts").unwrap();
  208. prop.push_u32(inst_rid)?;
  209. // TODO: when the plugin finishes, the instance ID should be cleared up somehow
  210. // both from the resource manager and from the property
  211. // https://www.chromium.org/developers/design-documents/inter-process-communication/
  212. let mut reply = vec![];
  213. inst_rid.encode(&mut reply).unwrap();
  214. Ok(reply)
  215. }
  216. }