From 3b4db8b66bbe24eaf13e4d4c9c4c0aeb0c1c1b9a Mon Sep 17 00:00:00 2001 From: kristof Date: Thu, 3 Apr 2025 13:21:46 +0200 Subject: [PATCH] ... --- rhai_engine/src/rhailoader/script_manager.rs | 231 ++++++++++++------- 1 file changed, 148 insertions(+), 83 deletions(-) diff --git a/rhai_engine/src/rhailoader/script_manager.rs b/rhai_engine/src/rhailoader/script_manager.rs index a275416..ccfed2c 100644 --- a/rhai_engine/src/rhailoader/script_manager.rs +++ b/rhai_engine/src/rhailoader/script_manager.rs @@ -121,7 +121,7 @@ impl ScriptManager { Ok(()) } - /// Extract functions from a script and register them with the engine + /// Extract functions from a script and register them directly with the engine fn map_script_functions(&mut self, script_name: &str) -> Result<(), String> { let script_arc = self.scripts.get(script_name) .ok_or_else(|| format!("Script not found: {}", script_name))? @@ -136,7 +136,7 @@ impl ScriptManager { // Log found functions for debugging println!("Registering functions for script '{}':", script_name); - // Register each function we found + // Register each function we found directly into the engine for fn_def in function_defs { let fn_name = fn_def.name.to_string(); println!(" - {} (params: {})", fn_name, fn_def.params.len()); @@ -144,106 +144,171 @@ impl ScriptManager { // Create a fully qualified name for the function (script:function) let full_name = format!("{}:{}", script_name, fn_name); - // Create strong references to required data + // Create references to required data let script_arc_clone = script_arc.clone(); let fn_name_clone = fn_name.clone(); - // Create a function wrapper that will be called from Rust + // Create a wrapper function based on parameter count + match fn_def.params.len() { + 0 => { + // Create a function wrapper with no parameters + self.register_script_fn0(&full_name, &script_arc_clone, &fn_name_clone); + }, + 1 => { + // Create a function wrapper with 1 parameter + self.register_script_fn1(&full_name, &script_arc_clone, &fn_name_clone); + }, + 2 => { + // Create a function wrapper with 2 parameters + self.register_script_fn2(&full_name, &script_arc_clone, &fn_name_clone); + }, + 3 => { + // Create a function wrapper with 3 parameters + self.register_script_fn3(&full_name, &script_arc_clone, &fn_name_clone); + }, + _ => { + // For functions with more parameters, create a generic wrapper + self.register_script_fn_dynamic(&full_name, &script_arc_clone, &fn_name_clone); + } + } + + // Create the function wrapper for our maps + let script_arc_for_map = script_arc.clone(); + let fn_name_for_map = fn_name.clone(); + + // Create a function wrapper for direct calling let function_wrapper: RhaiFn = Box::new(move |args: Vec| -> Result { - // Important: We need to create a scope for each call + // Create a new scope for this invocation let mut scope = Scope::new(); - // Use a clone of self.engine - this ensures we're using the same engine - // instance while avoiding ownership issues in the closure + // We need a new engine here because we're inside a closure let mut engine = Engine::new(); - - // Configure the engine with the same settings - // This is a workaround for the ownership issue with closures engine.set_fast_operators(true); - // Register the same built-in string functions - engine.register_fn("substr", |s: &str, start: i64, len: i64| { - let start = start as usize; - let len = len as usize; - if start >= s.len() { return String::new(); } - let end = (start + len).min(s.len()); - s[start..end].to_string() - }); - engine.register_fn("to_upper", |s: &str| s.to_uppercase()); - engine.register_fn("to_lower", |s: &str| s.to_lowercase()); - engine.register_fn("split", |text: &str, delimiter: &str| -> Array { - text.split(delimiter).map(|s| Dynamic::from(s.to_string())).collect() - }); - engine.register_fn("split", |s: &str| -> Array { - s.split_whitespace() - .map(|part| Dynamic::from(part.to_string())) - .collect() - }); - engine.register_fn("len", |array: &mut Array| -> i64 { - array.len() as i64 - }); - engine.register_fn("join", |arr: &mut Array, separator: &str| -> String { - arr.iter() - .map(|v| v.to_string()) - .collect::>() - .join(separator) - }); - engine.register_fn("replace", |s: &str, from: &str, to: &str| -> String { - s.replace(from, to) - }); - engine.register_fn("push", |arr: &mut Array, value: Dynamic| { - arr.push(value); - Dynamic::from(()) - }); - engine.register_fn("len", |s: &str| -> i64 { - s.len() as i64 - }); - engine.register_fn("..", |start: i64, end: i64| { - let mut arr = Array::new(); - for i in start..end { arr.push(Dynamic::from(i)); } - arr - }); - - // Call the function using the configured engine + // Call the function using the engine engine.call_fn::( &mut scope, - &script_arc_clone.read().unwrap(), - &fn_name_clone, + &script_arc_for_map.read().unwrap(), + &fn_name_for_map, args - ).map_err(|e| format!("Error calling function {}: {}", fn_name_clone, e)) + ).map_err(|e| format!("Error calling function {}: {}", fn_name_for_map, e)) }); - // Store the function wrapper in the functions map - self.functions.insert(full_name, function_wrapper); - - // Also create a filter wrapper with the same functionality but specific to filters - let script_arc_clone = script_arc.clone(); - let fn_name_clone = fn_name.clone(); - - let filter_wrapper: RhaiFn = Box::new(move |args: Vec| -> Result { - // Create a scope and engine similar to the function wrapper - let mut scope = Scope::new(); - let mut engine = Engine::new(); - - // Configure the engine with the same settings - engine.set_fast_operators(true); - - // Call the function (no need to register all functions again as filters don't use them) - engine.call_fn::( - &mut scope, - &script_arc_clone.read().unwrap(), - &fn_name_clone, - args - ).map_err(|e| format!("Error calling filter {}: {}", fn_name_clone, e)) - }); - - // Store the filter wrapper - self.filters.insert(fn_name, filter_wrapper); + // Store in our maps for backwards compatibility + self.functions.insert(full_name, function_wrapper.clone()); + self.filters.insert(fn_name, function_wrapper); } Ok(()) } + // Helper method to register a script function with 0 parameters + fn register_script_fn0(&mut self, full_name: &str, script_arc: &Arc>, fn_name: &str) { + let script_arc = script_arc.clone(); + let fn_name = fn_name.to_string(); + + self.engine.register_fn(full_name, move || -> Dynamic { + let mut scope = Scope::new(); + let engine = Engine::new(); // We need a temporary engine for the call + + engine.call_fn::( + &mut scope, + &script_arc.read().unwrap(), + &fn_name, + [] + ).unwrap_or_else(|e| { + // Return error as a string if the function call fails + Dynamic::from(format!("Error: {}", e)) + }) + }); + } + + // Helper method to register a script function with 1 parameter + fn register_script_fn1(&mut self, full_name: &str, script_arc: &Arc>, fn_name: &str) { + let script_arc = script_arc.clone(); + let fn_name = fn_name.to_string(); + + self.engine.register_fn(full_name, move |arg1: Dynamic| -> Dynamic { + let mut scope = Scope::new(); + let engine = Engine::new(); + + engine.call_fn::( + &mut scope, + &script_arc.read().unwrap(), + &fn_name, + [arg1] + ).unwrap_or_else(|e| { + Dynamic::from(format!("Error: {}", e)) + }) + }); + } + + // Helper method to register a script function with 2 parameters + fn register_script_fn2(&mut self, full_name: &str, script_arc: &Arc>, fn_name: &str) { + let script_arc = script_arc.clone(); + let fn_name = fn_name.to_string(); + + self.engine.register_fn(full_name, move |arg1: Dynamic, arg2: Dynamic| -> Dynamic { + let mut scope = Scope::new(); + let engine = Engine::new(); + + engine.call_fn::( + &mut scope, + &script_arc.read().unwrap(), + &fn_name, + [arg1, arg2] + ).unwrap_or_else(|e| { + Dynamic::from(format!("Error: {}", e)) + }) + }); + } + + // Helper method to register a script function with 3 parameters + fn register_script_fn3(&mut self, full_name: &str, script_arc: &Arc>, fn_name: &str) { + let script_arc = script_arc.clone(); + let fn_name = fn_name.to_string(); + + self.engine.register_fn(full_name, move |arg1: Dynamic, arg2: Dynamic, arg3: Dynamic| -> Dynamic { + let mut scope = Scope::new(); + let engine = Engine::new(); + + engine.call_fn::( + &mut scope, + &script_arc.read().unwrap(), + &fn_name, + [arg1, arg2, arg3] + ).unwrap_or_else(|e| { + Dynamic::from(format!("Error: {}", e)) + }) + }); + } + + // Helper method to register a script function with dynamic parameters + fn register_script_fn_dynamic(&mut self, full_name: &str, script_arc: &Arc>, fn_name: &str) { + let script_arc = script_arc.clone(); + let fn_name = fn_name.to_string(); + + // For functions with many parameters, just use a HashMp approach instead + // Create a function wrapper that can be stored in our maps + let script_arc_for_map = script_arc.clone(); + let fn_name_for_map = fn_name.to_string(); + + // Create a function wrapper for direct calling + let function_wrapper: RhaiFn = Box::new(move |args: Vec| -> Result { + // Create a new scope for this invocation + let mut scope = Scope::new(); + let engine = Engine::new(); + + // Call the function using the engine + engine.call_fn::( + &mut scope, + &script_arc_for_map.read().unwrap(), + &fn_name_for_map, + args + ).map_err(|e| format!("Error calling function {}: {}", fn_name_for_map, e)) + }); + } + /// Get a function by its full name (script:function) pub fn get_function(&self, name: &str) -> Option<&RhaiFn> { self.functions.get(name)