tirbofish/dropbear · diff
input detection and more docs
Signature present but could not be verified.
Unverified
@@ -41,6 +41,8 @@ If you do not want to build it locally, you are able to download the latest acti Depsite it looking like a dependency for `eucalyptus`, it can serve as a framework too. Looking through the `docs.rs` will you find related documentation onhow to use it and for rendering your own projects. +The rhai reference for the eucalyptus editor is under the /docs folder of this repository, so take a look there. [Here is the entrance](https://github.com/4tkbytes/dropbear/blob/main/docs/README.md) + ## Compability | | Windows | macOS | Linux | Web | Android | iOS | @@ -8,3 +8,7 @@ Related links: [https://rhai.rs/book](https://rhai.rs/book) and [https://rhai.rs --- +| Math | https://github.com/4tkbytes/dropbear/blob/main/docs/math.md | +|-----------------|------------------------------------------------------------------| +| Utils | https://github.com/4tkbytes/dropbear/blob/main/docs/utils.md | +| Transformations | https://github.com/4tkbytes/dropbear/blob/main/docs/transform.md | @@ -0,0 +1,2 @@ +# Input + @@ -23,6 +23,7 @@ Just a list of stuff i need to work on/a list of items i need to implement - [ ] autosave (would be easy to impl) - [ ] attaching texture onto model - [ ] user defined keybinds +- [ ] console tab ## fixing @@ -0,0 +1,117 @@ +# Transform + +This page documents the functions and types related to position, rotation, and scale manipulation. + +## Types + +### Transform +Represents a 3D transformation, including position, rotation, and scale. + +### Quaternion +Represents a quaternion rotation (DQuat). + +### Vector3 +Represents a 3D vector (DVec3). + +## Functions + +### new_transform +Creates a new `Transform` with default values. + +#### Example +```rhai +let t = new_transform(); +``` + +### new_vec3 +Creates a new `Vector3` with the given x, y, z values, or returns a vector with all components set to 1.0. + +#### Parameters +- `x` - An f64 value for the x component. +- `y` - An f64 value for the y component. +- `z` - An f64 value for the z component. + +#### Example +```rhai +let v = new_vec3(1.0, 2.0, 3.0); +let v = new_vec3(); // returns Vector3(1.0, 1.0, 1.0) +``` + +### new_quat +Creates a new `Quaternion` with given angle and axis values, or returns an identity Quaternion. + +#### Parameters + +- `axis` - A Vector3 axis value. +- `angle` - The angle of rotation in radians. + +#### Example +```rhai +let q = new_quat(new_vec3(), degrees_to_radians(90)); +let q = new_quat(); +``` + +## Properties + +### Transform + +#### position +Gets or sets the position of the transform as a `Vector3`. + +#### Example +```rhai +let t = new_transform(); +t.position = new_vec3(1.0, 2.0, 3.0); +let pos = t.position; +``` + +#### rotation +Gets or sets the rotation of the transform as a `Quaternion`. + +#### Example +```rhai +let t = new_transform(); +t.rotation = some_quaternion; +let rot = t.rotation; +``` + +#### scale +Gets or sets the scale of the transform as a `Vector3`. + +#### Example +```rhai +let t = new_transform(); +t.scale = new_vec3(2.0, 2.0, 2.0); +let s = t.scale; +``` + +### Vector3 + +#### x +Gets or sets the x component. + +#### Example +```rhai +let v = new_vec3(1.0, 2.0, 3.0); +v.x = 5.0; +let x = v.x; +``` + +#### y +Gets or sets the y component. + +#### Example +```rhai +let v = new_vec3(1.0, 2.0, 3.0); +v.y = 6.0; +let y = v.y; +``` + +#### z +Gets or sets the z component. + +#### Example +```rhai +let v = new_vec3(1.0, 2.0, 3.0); +v.z = 7.0; +``` @@ -1,9 +1,11 @@ # Utils -In this page, it will show you the different utility functions available. +In this page, it will show you the different utility functions available. ## log -The log function is used to (as the name suggests) log a string to the console. + +The log function is used to (as the name suggests) log a string to the console. + ### Parameters - `str` - The message to be printed @@ -15,12 +17,14 @@ log("Hello log function!"); ``` ## time -The time function returns an `f64` value to the current `UNIX_EPOCH` time of the computer running. This is particularly useful for stopwatches and comparing time. +The time function returns an `f64` value to the current `UNIX_EPOCH` time of the computer running. This is particularly useful for stopwatches and comparing time. ### Parameters + No params for this one! ### Example + ```rhai time() // returns the current unix_epoch time ``` @@ -204,19 +204,19 @@ impl<'a> TabViewer for EditorTabViewer<'a> { camera.zfar, ); - log::debug!( - "Camera - eye: {:?}, target: {:?}, up: {:?}", - camera.eye, - camera.target, - camera.up - ); - log::debug!( - "Camera - fov_y: {:.3}°, aspect: {:.3}, znear: {:.3}, zfar: {:.3}", - camera.fov_y, - camera.aspect, - camera.znear, - camera.zfar - ); + // log::debug!( + // "Camera - eye: {:?}, target: {:?}, up: {:?}", + // camera.eye, + // camera.target, + // camera.up + // ); + // log::debug!( + // "Camera - fov_y: {:.3}°, aspect: {:.3}, znear: {:.3}, zfar: {:.3}", + // camera.fov_y, + // camera.aspect, + // camera.znear, + // camera.zfar + // ); if !view_matrix.is_finite() { log::error!("Invalid view matrix"); @@ -265,13 +265,13 @@ impl<'a> TabViewer for EditorTabViewer<'a> { return; } - log::debug!( - "Click pos: {:?}, NDC: ({:.3}, {:.3})", - click_pos, - ndc_x, - ndc_y - ); - log::debug!("Ray start: {:?}, direction: {:?}", ray_start, ray_direction); + // log::debug!( + // "Click pos: {:?}, NDC: ({:.3}, {:.3})", + // click_pos, + // ndc_x, + // ndc_y + // ); + // log::debug!("Ray start: {:?}, direction: {:?}", ray_start, ray_direction); let mut closest_distance = f64::INFINITY; let mut selected_entity_id: Option<hecs::Entity> = None; @@ -287,28 +287,28 @@ impl<'a> TabViewer for EditorTabViewer<'a> { let to_sphere = entity_pos - ray_start; let projection = to_sphere.dot(ray_direction); - log::debug!( - "Entity {:?}: pos={:?}, scale={:?}, radius={:.3}", - entity_id, - entity_pos, - transform.scale, - sphere_radius - ); - log::debug!( - " to_sphere={:?}, projection={:.3}", - to_sphere, - projection - ); + // log::debug!( + // "Entity {:?}: pos={:?}, scale={:?}, radius={:.3}", + // entity_id, + // entity_pos, + // transform.scale, + // sphere_radius + // ); + // log::debug!( + // " to_sphere={:?}, projection={:.3}", + // to_sphere, + // projection + // ); if projection > 0.0 { let closest_point = ray_start + ray_direction * projection; let distance_to_sphere = (closest_point - entity_pos).length(); - log::debug!( - " closest_point={:?}, distance_to_sphere={:.3}", - closest_point, - distance_to_sphere - ); + // log::debug!( + // " closest_point={:?}, distance_to_sphere={:.3}", + // closest_point, + // distance_to_sphere + // ); if distance_to_sphere <= sphere_radius { let discriminant = sphere_radius * sphere_radius @@ -316,10 +316,10 @@ impl<'a> TabViewer for EditorTabViewer<'a> { if discriminant >= 0.0 { let intersection_distance = projection - discriminant.sqrt(); - log::debug!( - " HIT! intersection_distance={:.3}", - intersection_distance - ); + // log::debug!( + // " HIT! intersection_distance={:.3}", + // intersection_distance + // ); if intersection_distance < closest_distance { closest_distance = intersection_distance; @@ -327,14 +327,14 @@ impl<'a> TabViewer for EditorTabViewer<'a> { } } } else { - log::debug!( - " MISS: distance {:.3} > radius {:.3}", - distance_to_sphere, - sphere_radius - ); + // log::debug!( + // " MISS: distance {:.3} > radius {:.3}", + // distance_to_sphere, + // sphere_radius + // ); } } else { - log::debug!(" BEHIND: projection {:.3} <= 0", projection); + // log::debug!(" BEHIND: projection {:.3} <= 0", projection); } } @@ -13,17 +13,17 @@ use winit::{ impl Keyboard for Editor { fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) { #[cfg(not(target_os = "macos"))] - let ctrl_pressed = self.pressed_keys.contains(&KeyCode::ControlLeft) - || self.pressed_keys.contains(&KeyCode::ControlRight); + let ctrl_pressed = self.input_state.pressed_keys.contains(&KeyCode::ControlLeft) + || self.input_state.pressed_keys.contains(&KeyCode::ControlRight); #[cfg(target_os = "macos")] - let ctrl_pressed = self.pressed_keys.contains(&KeyCode::SuperLeft) - || self.pressed_keys.contains(&KeyCode::SuperRight); + let ctrl_pressed = self.input_state.pressed_keys.contains(&KeyCode::SuperLeft) + || self.input_state.pressed_keys.contains(&KeyCode::SuperRight); - let _alt_pressed = self.pressed_keys.contains(&KeyCode::AltLeft) - || self.pressed_keys.contains(&KeyCode::AltRight); + let _alt_pressed = self.input_state.pressed_keys.contains(&KeyCode::AltLeft) + || self.input_state.pressed_keys.contains(&KeyCode::AltRight); - let shift_pressed = self.pressed_keys.contains(&KeyCode::ShiftLeft) - || self.pressed_keys.contains(&KeyCode::ShiftRight); + let shift_pressed = self.input_state.pressed_keys.contains(&KeyCode::ShiftLeft) + || self.input_state.pressed_keys.contains(&KeyCode::ShiftRight); let is_double_press = self.is_double_key_press(key); @@ -39,7 +39,7 @@ impl Keyboard for Editor { window.set_cursor_visible(true); } } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::KeyF => { @@ -57,7 +57,7 @@ impl Keyboard for Editor { let _ = window.set_cursor_position(center); } } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::Delete => { @@ -68,7 +68,7 @@ impl Keyboard for Editor { crate::warn!("Failed to delete: No entity selected"); } } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::Escape => { @@ -84,7 +84,7 @@ impl Keyboard for Editor { window.set_cursor_visible(true); } } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::KeyQ => { @@ -138,7 +138,7 @@ impl Keyboard for Editor { crate::info!("GizmoMode set to scale"); self.gizmo_mode = GizmoMode::all_scale(); } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::KeyV => { @@ -153,7 +153,7 @@ impl Keyboard for Editor { } } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::KeyS => { @@ -172,7 +172,7 @@ impl Keyboard for Editor { } } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::KeyZ => { @@ -188,7 +188,7 @@ impl Keyboard for Editor { crate::info!("GizmoMode set to translate"); self.gizmo_mode = GizmoMode::all_translate(); } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } KeyCode::F1 => { @@ -205,17 +205,17 @@ impl Keyboard for Editor { crate::info!("GizmoMode set to rotate"); self.gizmo_mode = GizmoMode::all_rotate(); } else { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } _ => { - self.pressed_keys.insert(key); + self.input_state.pressed_keys.insert(key); } } } fn key_up(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) { - self.pressed_keys.remove(&key); + self.input_state.pressed_keys.remove(&key); } } @@ -238,17 +238,17 @@ impl Mouse for Editor { window.set_cursor_visible(false); } } - self.mouse_pos = (position.x, position.y); + self.input_state.mouse_pos = (position.x, position.y); } fn mouse_down(&mut self, button: MouseButton) { match button { - _ => { self.mouse_button.insert(button); } + _ => { self.input_state.mouse_button.insert(button); } } } fn mouse_up(&mut self, button: MouseButton) { - self.mouse_button.remove(&button); + self.input_state.mouse_button.remove(&button); } } @@ -25,14 +25,14 @@ use log; use parking_lot::Mutex; use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode}; use wgpu::{Color, Extent3d, RenderPipeline}; -use winit::{event::MouseButton, keyboard::KeyCode, window::Window}; +use winit::{keyboard::KeyCode, window::Window}; use crate::{ camera::{ CameraAction, CameraManager, CameraType, DebugCameraController, PlayerCameraController, }, - scripting::{ScriptAction, ScriptManager}, - states::{EntityNode, ModelProperties, PROJECT, SCENES, SceneEntity, ScriptComponent}, + scripting::{input::InputState, ScriptAction, ScriptManager}, + states::{EntityNode, ModelProperties, SceneEntity, ScriptComponent, PROJECT, SCENES}, utils::ViewportMode, }; @@ -46,10 +46,8 @@ pub struct Editor { color: Color, camera_manager: CameraManager, - mouse_delta: Option<(f64, f64)>, is_viewport_focused: bool, - pressed_keys: HashSet<KeyCode>, // is_cursor_locked: bool, window: Option<Arc<Window>>, @@ -66,16 +64,21 @@ pub struct Editor { undo_stack: Vec<UndoableAction>, // todo: add redo (later) // redo_stack: Vec<UndoableAction>, - last_key_press_times: HashMap<KeyCode, Instant>, - double_press_threshold: Duration, - mouse_pos: (f64, f64), - mouse_button: HashSet<MouseButton>, + + // last_key_press_times: HashMap<KeyCode, Instant>, + // double_press_threshold: Duration, + // mouse_pos: (f64, f64), + // mouse_button: HashSet<MouseButton>, + // pressed_keys: HashSet<KeyCode>, + // mouse_delta: Option<(f64, f64)>, editor_state: EditorState, gizmo_mode: EnumSet<GizmoMode>, script_manager: ScriptManager, play_mode_backup: Option<PlayModeBackup>, + + input_state: InputState, } #[derive(Clone)] @@ -214,7 +217,6 @@ impl Editor { camera_manager: CameraManager::new(), color: Color::default(), is_viewport_focused: false, - pressed_keys: HashSet::new(), // is_cursor_locked: false, window: None, world: World::new(), @@ -227,15 +229,17 @@ impl Editor { viewport_mode: ViewportMode::None, signal: Signal::None, undo_stack: Vec::new(), - last_key_press_times: HashMap::new(), - double_press_threshold: Duration::from_millis(300), script_manager: ScriptManager::new(), - mouse_delta: None, editor_state: EditorState::Editing, gizmo_mode: EnumSet::empty(), - mouse_pos: Default::default(), - mouse_button: Default::default(), play_mode_backup: None, + input_state: InputState::new(), + // mouse_pos: Default::default(), + // mouse_button: Default::default(), + // pressed_keys: HashSet::new(), + // last_key_press_times: HashMap::new(), + // double_press_threshold: Duration::from_millis(300), + // mouse_delta: None, // ..Default::default() // note to self: DO NOT USE ..DEFAULT::DEFAULT(), IT WILL CAUSE OVERFLOW } @@ -244,16 +248,16 @@ impl Editor { fn is_double_key_press(&mut self, key: KeyCode) -> bool { let now = Instant::now(); - if let Some(last_time) = self.last_key_press_times.get(&key) { + if let Some(last_time) = self.input_state.last_key_press_times.get(&key) { let time_diff = now.duration_since(*last_time); - if time_diff <= self.double_press_threshold { - self.last_key_press_times.remove(&key); + if time_diff <= self.input_state.double_press_threshold { + self.input_state.last_key_press_times.remove(&key); return true; } } - self.last_key_press_times.insert(key, now); + self.input_state.last_key_press_times.insert(key, now); false } // pub fn save_project_config(&self) -> anyhow::Result<()> { @@ -118,7 +118,7 @@ impl Scene for Editor { } if matches!(self.editor_state, EditorState::Playing) { - if self.pressed_keys.contains(&KeyCode::Escape) { + if self.input_state.pressed_keys.contains(&KeyCode::Escape) { self.signal = Signal::StopPlaying; } @@ -132,7 +132,7 @@ impl Scene for Editor { } for (entity_id, script_name) in script_entities { - if let Err(e) = self.script_manager.update_entity_script(entity_id, &script_name, &mut self.world, dt) { + if let Err(e) = self.script_manager.update_entity_script(entity_id, &script_name, &mut self.world, &self.input_state, dt) { log::warn!("Failed to update script '{}' for entity {:?}: {}", script_name, entity_id, e); } } @@ -143,6 +143,7 @@ impl Scene for Editor { // && self.is_using_debug_camera() { let movement_keys: std::collections::HashSet<KeyCode> = self + .input_state .pressed_keys .iter() .filter(|&&key| { @@ -160,7 +161,7 @@ impl Scene for Editor { .collect(); self.camera_manager - .handle_input(&movement_keys, self.mouse_delta); + .handle_input(&movement_keys, self.input_state.mouse_delta); } match &self.signal { @@ -405,7 +406,7 @@ impl Scene for Editor { for (entity_id, script) in script_entities { match self.script_manager.load_script(&script.path) { Ok(script_name) => { - if let Err(e) = self.script_manager.init_entity_script(entity_id, &script_name, &mut self.world) { + if let Err(e) = self.script_manager.init_entity_script(entity_id, &script_name, &mut self.world, &self.input_state) { log::warn!("Failed to initialise script '{}' for entity {:?}: {}", script.name, entity_id, e); } } @@ -0,0 +1,101 @@ +use std::{collections::{HashMap, HashSet}, time::{Duration, Instant}}; + +use rhai::*; +use winit::{event::MouseButton, keyboard::KeyCode}; + +#[derive(rhai::CustomType, Clone)] +pub struct InputState { + pub last_key_press_times: HashMap<KeyCode, Instant>, + pub double_press_threshold: Duration, + pub mouse_pos: (f64, f64), + pub mouse_button: HashSet<MouseButton>, + pub pressed_keys: HashSet<KeyCode>, + pub mouse_delta: Option<(f64, f64)>, +} + +impl Default for InputState { + fn default() -> Self { + Self::new() + } +} + +impl InputState { + pub fn new() -> Self { + Self { + mouse_pos: Default::default(), + mouse_button: Default::default(), + pressed_keys: HashSet::new(), + last_key_press_times: HashMap::new(), + double_press_threshold: Duration::from_millis(300), + mouse_delta: None, + } + } + + pub fn register_input_modules(engine: &mut Engine) { + engine.register_type_with_name::<InputState>("InputState"); + engine.register_type_with_name::<Key>("Key"); + + engine + .register_fn("is_pressed", |s: &mut InputState, k: Key| s.pressed_keys.contains(&k.0)) + .register_get("mouse_x", |s: &mut InputState| s.mouse_pos.0) + .register_get("mouse_y", |s: &mut InputState| s.mouse_pos.1) + .register_get("has_mouse_delta", |s: &mut InputState| s.mouse_delta.is_some()) + .register_fn("mouse_dx", |s: &mut InputState| s.mouse_delta.map(|d| d.0).unwrap_or(0.0)) + .register_fn("mouse_dy", |s: &mut InputState| s.mouse_delta.map(|d| d.1).unwrap_or(0.0)); + + let mut kmod = Module::new(); + + macro_rules! add_key { + ($name:ident, $kc:expr) => { + kmod.set_var(stringify!($name), Key($kc)); + }; + } + + add_key!(A, KeyCode::KeyA); + add_key!(B, KeyCode::KeyB); + add_key!(C, KeyCode::KeyC); + add_key!(D, KeyCode::KeyD); + add_key!(E, KeyCode::KeyE); + add_key!(F, KeyCode::KeyF); + add_key!(G, KeyCode::KeyG); + add_key!(H, KeyCode::KeyH); + add_key!(I, KeyCode::KeyI); + add_key!(J, KeyCode::KeyJ); + add_key!(K, KeyCode::KeyK); + add_key!(L, KeyCode::KeyL); + add_key!(M, KeyCode::KeyM); + add_key!(N, KeyCode::KeyN); + add_key!(O, KeyCode::KeyO); + add_key!(P, KeyCode::KeyP); + add_key!(Q, KeyCode::KeyQ); + add_key!(R, KeyCode::KeyR); + add_key!(S, KeyCode::KeyS); + add_key!(T, KeyCode::KeyT); + add_key!(U, KeyCode::KeyU); + add_key!(V, KeyCode::KeyV); + add_key!(W, KeyCode::KeyW); + add_key!(X, KeyCode::KeyX); + add_key!(Y, KeyCode::KeyY); + add_key!(Z, KeyCode::KeyZ); + + add_key!(Space, KeyCode::Space); + add_key!(ShiftLeft, KeyCode::ShiftLeft); + add_key!(ShiftRight, KeyCode::ShiftRight); + add_key!(ControlLeft, KeyCode::ControlLeft); + add_key!(ControlRight, KeyCode::ControlRight); + add_key!(AltLeft, KeyCode::AltLeft); + add_key!(AltRight, KeyCode::AltRight); + add_key!(Escape, KeyCode::Escape); + add_key!(Enter, KeyCode::Enter); + add_key!(Tab, KeyCode::Tab); + add_key!(Up, KeyCode::ArrowUp); + add_key!(Down, KeyCode::ArrowDown); + add_key!(Left, KeyCode::ArrowLeft); + add_key!(Right, KeyCode::ArrowRight); + + engine.register_static_module("keys", kmod.into()); + } +} + +#[derive(rhai::CustomType, Clone, Copy)] +pub struct Key(pub KeyCode); @@ -56,5 +56,5 @@ pub mod math_functions { } pub fn register_math_functions(engine: &mut Engine) { - engine.register_global_module(exported_module!(math_functions).into()); + engine.register_static_module("math", exported_module!(math_functions).into()); } @@ -1,4 +1,5 @@ pub mod math; +pub mod input; use dropbear_engine::entity::{AdoptedEntity, Transform}; use glam::{DQuat, DVec3}; @@ -154,7 +155,6 @@ pub fn attach_script_to_entity( Ok(()) } -#[allow(dead_code)] pub struct ScriptManager { pub engine: rhai::Engine, compiled_scripts: HashMap<String, AST>, @@ -167,6 +167,7 @@ impl ScriptManager { // REGISTER FUNCTIONS HERE math::register_math_functions(&mut engine); + input::InputState::register_input_modules(&mut engine); engine.register_type_with_name::<Transform>("Transform"); engine.register_type_with_name::<DQuat>("Quaternion"); @@ -236,6 +237,7 @@ impl ScriptManager { entity_id: hecs::Entity, script_name: &str, world: &mut World, + input_state: &input::InputState, ) -> anyhow::Result<()> { if let Some(ast) = self.compiled_scripts.get(script_name) { let mut scope = Scope::new(); @@ -244,6 +246,8 @@ impl ScriptManager { scope.push("transform", *transform); } + scope.push("input", input_state.clone()); + if let Ok(_) = self.engine.call_fn::<()>(&mut scope, ast, "init", ()) { log::debug!("Called init for entity {:?}", entity_id); } @@ -260,6 +264,7 @@ impl ScriptManager { entity_id: hecs::Entity, script_name: &str, world: &mut World, + input_state: &input::InputState, dt: f32, ) -> anyhow::Result<()> { if let Some(ast) = self.compiled_scripts.get(script_name) { @@ -268,6 +273,8 @@ impl ScriptManager { scope.set_value("transform", *transform); } + scope.set_value("input", input_state.clone()); + match self.engine.call_fn::<()>(scope, ast, "update", (dt,)) { Ok(_) => { match scope.get_value::<Transform>("transform") { @@ -292,6 +299,7 @@ impl ScriptManager { } } else { log::error!("Unable to fetch compiled scripts for entity {:?}. Script Name: {}", entity_id, script_name); + println!("{:#?}", self.compiled_scripts); } Ok(()) } @@ -304,7 +312,10 @@ impl ScriptManager { .to_string_lossy() .to_string(); - let ast = self.engine.compile(&script_content)?; + let ast = self.engine.compile(&script_content).map_err(|e| { + log::error!("Compiling AST for script path [{}] returned an error: {}", script_path.display(), e); + e + })?; self.compiled_scripts.insert(script_name.clone(), ast); log::info!("Loaded script: {}", script_name);