plugin.rs 7.6 KB

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