kitgit

tirbofish/dropbear · commit

35dbc3bc0532faf94bb353f5e6037c36e68611cd

im assuming it still works?

Unverified

tk <4tkbytes@pm.me> · 2025-09-01 05:01

view full diff

diff --git a/Cargo.toml b/Cargo.toml
index 200086b..c73c479 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -40,14 +40,11 @@ open = "5"
 parking_lot = {version = "0.12", features = ["deadlock_detection"] }
 pollster = "0.4"
 rfd = "0.15.4"
-rhai = "1.22"
 ron = "0.10.1"
 russimp-ng = { version = "3.2.4", features = ["static-link"] }
 rustyscript = { version = "0.12" }
 serde = { version = "1.0.219", features = ["derive"] }
 spin_sleep = "1.3"
-specta = { version = "=2.0.0-rc.22", features = ["glam", "derive", "export"] }
-specta-typescript = "0.0.9"
 tokio = { version = "1", features = ["full"] }
 transform-gizmo-egui = { git = "https://github.com/4tkbytes/transform-gizmo"}
 wgpu = "26"
@@ -60,13 +57,6 @@ version = "0.25"
 default-features = false
 features = ["png"]
 
-[patch.crates-io]
-dropbear-engine = { path = "dropbear-engine" }
-eucalyptus = { path = "eucalyptus" }
-# fixing android compilation
-russimp-sys-ng = { git = "https://github.com/Kek5chen/russimp-sys-ng", branch = "fix-android-compilation" }
-
-
 [profile.dev]
 opt-level = 0
 debug = true
diff --git a/dropbear-engine/Cargo.toml b/dropbear-engine/Cargo.toml
index 860d7ed..a3cbcd5 100644
--- a/dropbear-engine/Cargo.toml
+++ b/dropbear-engine/Cargo.toml
@@ -23,7 +23,6 @@ gilrs.workspace = true
 glam.workspace = true
 log.workspace = true
 log-once.workspace = true
-#rfd.workspace = true
 russimp-ng.workspace = true
 pollster.workspace = true
 serde.workspace = true
@@ -34,7 +33,6 @@ hecs.workspace = true
 once_cell.workspace = true
 parking_lot.workspace = true
 lazy_static.workspace = true
-specta.workspace = true
 
 [target.'cfg(not(target_os = "android"))'.dependencies]
 rfd.workspace = true
diff --git a/eucalyptus/Cargo.toml b/eucalyptus/Cargo.toml
index 54ed5b5..6a17197 100644
--- a/eucalyptus/Cargo.toml
+++ b/eucalyptus/Cargo.toml
@@ -31,7 +31,6 @@ model_to_image = { workspace = true, optional = true }
 once_cell = { workspace = true, optional = true }
 open = { workspace = true, optional = true }
 parking_lot = { workspace = true, optional = true }
-rhai = { workspace = true, optional = true }
 ron = { workspace = true, optional = true }
 serde = { workspace = true, optional = true }
 tokio = { workspace = true, optional = true }
@@ -44,8 +43,6 @@ zip = { workspace = true, optional = true }
 bytemuck = "1.23.2"
 
 rustyscript.workspace = true
-specta.workspace = true
-specta-typescript.workspace = true
 
 [target.'cfg(not(target_os = "android"))'.dependencies]
 rfd = { workspace = true, optional = true }
@@ -73,7 +70,6 @@ editor = [
     "open",
     "parking_lot",
     "rfd",
-    "rhai",
     "ron",
     "serde",
     "tokio",
@@ -94,7 +90,6 @@ data-only = [
     "glam",
     "log",
     "dropbear-engine",
-    "rhai",
     "winit",
     "hecs",
     "ron",
diff --git a/eucalyptus/build.rs b/eucalyptus/build.rs
index fd9e83f..4b31369 100644
--- a/eucalyptus/build.rs
+++ b/eucalyptus/build.rs
@@ -55,6 +55,7 @@ fn main() -> anyhow::Result<()> {
         ));
     }
 
+    println!("cargo:rustc-link-arg=/NODEFAULTLIB:libcmt.lib");
     println!("cargo:rerun-if-changed=build.rs");
     Ok(())
 }
diff --git a/eucalyptus/src/dropbear.ts b/eucalyptus/src/dropbear.ts
new file mode 100644
index 0000000..f8e1168
--- /dev/null
+++ b/eucalyptus/src/dropbear.ts
@@ -0,0 +1,257 @@
+// Modules for the dropbear-engine scripting component
+// Made by 4tkbytes
+// EDIT THIS IF YOU WISH, MOST LIKELY 
+
+type TransformT = any;
+type Vec3T = any;
+type QuatT = any;
+type InputStateT = any;
+
+/**
+ * A placeholder function that returns the value of the host function
+ */
+function hostFn(name: string) {
+    const fn = (globalThis as any)[name];
+    if (typeof fn !== "function") {
+        throw new Error(`Host function ${name}() is not available. Make sure the runtime exposes it.`);
+    }
+    return fn;
+}
+
+const Transform = {
+    create: (): TransformT => hostFn("createTransform")(),
+    translate: (transform: TransformT, translation: [number, number, number] | number[]): TransformT =>
+        hostFn("transformTranslate")(transform, translation),
+    rotateX: (transform: TransformT, angle: number): TransformT =>
+        hostFn("transformRotateX")(transform, angle),
+    rotateY: (transform: TransformT, angle: number): TransformT =>
+        hostFn("transformRotateY")(transform, angle),
+    rotateZ: (transform: TransformT, angle: number): TransformT =>
+        hostFn("transformRotateZ")(transform, angle),
+    scale: (transform: TransformT, scale: number | [number, number, number]): TransformT =>
+        hostFn("transformScale")(transform, scale),
+    matrix: (transform: TransformT): TransformT => hostFn("transformMatrix")(transform),
+};
+
+/**
+ * A Vector of 3 components: an X, Y and Z. 
+ */
+const Vec3 = {
+    new: (x = 0, y = 0, z = 0): Vec3T => hostFn("createVec3")(x, y, z),
+    zero: (): Vec3T => hostFn("createVec3")(0, 0, 0),
+    one: (): Vec3T => hostFn("createVec3")(1, 1, 1),
+};
+
+const Quaternion = {
+    identity: (): QuatT => hostFn("createQuatIdentity")(),
+    fromEuler: (x: number, y: number, z: number): QuatT => hostFn("createQuatFromEuler")(x, y, z),
+};
+
+export const Keys = {
+    // Letters
+    KeyA: "KeyA", KeyB: "KeyB", KeyC: "KeyC", KeyD: "KeyD", KeyE: "KeyE", KeyF: "KeyF", 
+    KeyG: "KeyG", KeyH: "KeyH", KeyI: "KeyI", KeyJ: "KeyJ", KeyK: "KeyK", KeyL: "KeyL", 
+    KeyM: "KeyM", KeyN: "KeyN", KeyO: "KeyO", KeyP: "KeyP", KeyQ: "KeyQ", KeyR: "KeyR", 
+    KeyS: "KeyS", KeyT: "KeyT", KeyU: "KeyU", KeyV: "KeyV", KeyW: "KeyW", KeyX: "KeyX", 
+    KeyY: "KeyY", KeyZ: "KeyZ",
+    
+    // Numbers
+    Digit0: "Digit0", Digit1: "Digit1", Digit2: "Digit2", Digit3: "Digit3", Digit4: "Digit4",
+    Digit5: "Digit5", Digit6: "Digit6", Digit7: "Digit7", Digit8: "Digit8", Digit9: "Digit9",
+    
+    // Special keys
+    Space: "Space",
+    ShiftLeft: "ShiftLeft",
+    ShiftRight: "ShiftRight",
+    ControlLeft: "ControlLeft",
+    ControlRight: "ControlRight",
+    AltLeft: "AltLeft",
+    AltRight: "AltRight",
+    Escape: "Escape",
+    Enter: "Enter",
+    Tab: "Tab",
+    ArrowUp: "ArrowUp",
+    ArrowDown: "ArrowDown",
+    ArrowLeft: "ArrowLeft",
+    ArrowRight: "ArrowRight",
+    
+    // Function keys
+    F1: "F1", F2: "F2", F3: "F3", F4: "F4", F5: "F5", F6: "F6",
+    F7: "F7", F8: "F8", F9: "F9", F10: "F10", F11: "F11", F12: "F12",
+} as const;
+
+export type KeyCode = typeof Keys[keyof typeof Keys];
+
+export const Input = {
+    /**
+     * Check if a key is currently pressed
+     * @param inputState The current input state
+     * @param key The key to check
+     * @returns true if the key is pressed, false otherwise
+     */
+    isKeyPressed: (inputState: InputStateT, key: KeyCode): boolean => 
+        hostFn("isKeyPressed")(inputState, key),
+
+    /**
+     * Get the current mouse X position
+     * @param inputState The current input state
+     * @returns The mouse X coordinate
+     */
+    getMouseX: (inputState: InputStateT): number => 
+        hostFn("getMouseX")(inputState),
+
+    /**
+     * Get the current mouse Y position
+     * @param inputState The current input state
+     * @returns The mouse Y coordinate
+     */
+    getMouseY: (inputState: InputStateT): number => 
+        hostFn("getMouseY")(inputState),
+
+    /**
+     * Get the mouse delta X (movement since last frame)
+     * @param inputState The current input state
+     * @returns The mouse delta X
+     */
+    getMouseDeltaX: (inputState: InputStateT): number => 
+        hostFn("getMouseDeltaX")(inputState),
+
+    /**
+     * Get the mouse delta Y (movement since last frame)
+     * @param inputState The current input state
+     * @returns The mouse delta Y
+     */
+    getMouseDeltaY: (inputState: InputStateT): number => 
+        hostFn("getMouseDeltaY")(inputState),
+
+    /**
+     * Lock or unlock the cursor
+     * @param locked Whether to lock the cursor
+     */
+    lockCursor: (locked: boolean): void => 
+        hostFn("lockCursor")(locked),
+};
+
+export const EntityProperties = {
+    /**
+     * Get a property value (generic)
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns The property value or null if not found
+     */
+    getProperty: (properties: any, key: string): any =>
+        hostFn("getProperty")(properties, key),
+
+    /**
+     * Set a string property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @param value The string value
+     * @returns Updated properties object
+     */
+    setPropertyString: (properties: any, key: string, value: string): any =>
+        hostFn("setPropertyString")(properties, key, value),
+
+    /**
+     * Set an integer property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @param value The integer value
+     * @returns Updated properties object
+     */
+    setPropertyInt: (properties: any, key: string, value: number): any =>
+        hostFn("setPropertyInt")(properties, key, value),
+
+    /**
+     * Set a float property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @param value The float value
+     * @returns Updated properties object
+     */
+    setPropertyFloat: (properties: any, key: string, value: number): any =>
+        hostFn("setPropertyFloat")(properties, key, value),
+
+    /**
+     * Set a boolean property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @param value The boolean value
+     * @returns Updated properties object
+     */
+    setPropertyBool: (properties: any, key: string, value: boolean): any =>
+        hostFn("setPropertyBool")(properties, key, value),
+
+    /**
+     * Set a Vec3 property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @param value The Vec3 value as [x, y, z] array
+     * @returns Updated properties object
+     */
+    setPropertyVec3: (properties: any, key: string, value: [number, number, number]): any =>
+        hostFn("setPropertyVec3")(properties, key, value),
+
+    /**
+     * Get a string property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns The string value or empty string if not found
+     */
+    getString: (properties: any, key: string): string =>
+        hostFn("getString")(properties, key),
+
+    /**
+     * Get an integer property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns The integer value or 0 if not found
+     */
+    getInt: (properties: any, key: string): number =>
+        hostFn("getInt")(properties, key),
+
+    /**
+     * Get a float property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns The float value or 0.0 if not found
+     */
+    getFloat: (properties: any, key: string): number =>
+        hostFn("getFloat")(properties, key),
+
+    /**
+     * Get a boolean property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns The boolean value or false if not found
+     */
+    getBool: (properties: any, key: string): boolean =>
+        hostFn("getBool")(properties, key),
+
+    /**
+     * Get a Vec3 property
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns The Vec3 value as [x, y, z] array or [0, 0, 0] if not found
+     */
+    getVec3: (properties: any, key: string): [number, number, number] =>
+        hostFn("getVec3")(properties, key),
+
+    /**
+     * Check if a property exists
+     * @param properties The entity properties object
+     * @param key The property key
+     * @returns True if the property exists, false otherwise
+     */
+    hasProperty: (properties: any, key: string): boolean =>
+        hostFn("hasProperty")(properties, key),
+};
+
+export { Transform, Vec3, Quaternion };
+
+globalThis.Transform = Transform;
+globalThis.Vec3 = Vec3;
+globalThis.Quaternion = Quaternion;
+globalThis.Input = Input;
+globalThis.Keys = Keys;
+globalThis.EntityProperties = EntityProperties;
\ No newline at end of file
diff --git a/eucalyptus/src/editor/component.rs b/eucalyptus/src/editor/component.rs
index 250a3a7..4c5aa30 100644
--- a/eucalyptus/src/editor/component.rs
+++ b/eucalyptus/src/editor/component.rs
@@ -420,7 +420,7 @@ impl Component for ScriptComponent {
                     ui.horizontal(|ui| {
                         if ui.button("Browse").clicked() {
                             if let Some(script_file) = rfd::FileDialog::new()
-                                .add_filter("Rhai Script", &["rhai"])
+                                .add_filter("Typescript", &["ts"])
                                 .pick_file()
                             {
                                 let script_name = script_file
@@ -437,8 +437,8 @@ impl Component for ScriptComponent {
 
                         if ui.button("New").clicked() {
                             if let Some(script_path) = rfd::FileDialog::new()
-                                .add_filter("Rhai Script", &["rhai"])
-                                .set_file_name(format!("{}_script.rhai", label))
+                                .add_filter("TypeScript", &["ts"])
+                                .set_file_name(format!("{}_script.ts", label))
                                 .save_file()
                             {
                                 match std::fs::write(&script_path, TEMPLATE_SCRIPT) {
diff --git a/eucalyptus/src/editor/mod.rs b/eucalyptus/src/editor/mod.rs
index 50f29ab..7c1a351 100644
--- a/eucalyptus/src/editor/mod.rs
+++ b/eucalyptus/src/editor/mod.rs
@@ -217,7 +217,7 @@ impl Editor {
             viewport_mode: ViewportMode::None,
             signal: Signal::None,
             undo_stack: Vec::new(),
-            script_manager: ScriptManager::new(),
+            script_manager: ScriptManager::new().unwrap(),
             editor_state: EditorState::Editing,
             gizmo_mode: EnumSet::empty(),
             play_mode_backup: None,
@@ -540,7 +540,6 @@ impl Editor {
                         viewport_mode: &mut self.viewport_mode,
                         undo_stack: &mut self.undo_stack,
                         signal: &mut self.signal,
-                        // engine: &mut self.rhai_engine,
                         camera_manager: &mut self.camera_manager,
                         gizmo_mode: &mut self.gizmo_mode,
                         editor_mode: &mut self.editor_state,
diff --git a/eucalyptus/src/scripting/entity.rs b/eucalyptus/src/scripting/entity.rs
index 86a9bf2..da73792 100644
--- a/eucalyptus/src/scripting/entity.rs
+++ b/eucalyptus/src/scripting/entity.rs
@@ -1,89 +1,333 @@
-use rhai::{Dynamic, Engine};
-
 use crate::states::{ModelProperties, PropertyValue};
+use rustyscript::{serde_json, Runtime};
+use serde::{Deserialize, Serialize};
+
+// Create a serializable version of ModelProperties for script communication
+#[derive(Clone, Serialize, Deserialize)]
+pub struct SerializableModelProperties {
+    pub properties: std::collections::HashMap<String, serde_json::Value>,
+}
+
+impl From<&ModelProperties> for SerializableModelProperties {
+    fn from(props: &ModelProperties) -> Self {
+        let mut properties = std::collections::HashMap::new();
+        
+        // Convert each property to JSON value
+        for (key, value) in props.custom_properties.iter() {
+            let json_value = match value {
+                PropertyValue::String(s) => serde_json::Value::String(s.clone()),
+                PropertyValue::Int(i) => serde_json::Value::Number(serde_json::Number::from(*i)),
+                PropertyValue::Float(f) => serde_json::Value::Number(
+                    serde_json::Number::from_f64(*f as f64).unwrap_or(serde_json::Number::from(0))
+                ),
+                PropertyValue::Bool(b) => serde_json::Value::Bool(*b),
+                PropertyValue::Vec3(v) => serde_json::Value::Array(vec![
+                    serde_json::Value::Number(serde_json::Number::from_f64(v[0] as f64).unwrap()),
+                    serde_json::Value::Number(serde_json::Number::from_f64(v[1] as f64).unwrap()),
+                    serde_json::Value::Number(serde_json::Number::from_f64(v[2] as f64).unwrap()),
+                ]),
+            };
+            properties.insert(key.clone(), json_value);
+        }
+        
+        Self { properties }
+    }
+}
+
+impl SerializableModelProperties {
+    pub fn to_model_properties(&self) -> ModelProperties {
+        let mut props = ModelProperties::default();
+        
+        for (key, value) in &self.properties {
+            let property_value = match value {
+                serde_json::Value::String(s) => PropertyValue::String(s.clone()),
+                serde_json::Value::Number(n) => {
+                    if let Some(i) = n.as_i64() {
+                        PropertyValue::Int(i)
+                    } else if let Some(f) = n.as_f64() {
+                        PropertyValue::Float(f)
+                    } else {
+                        continue;
+                    }
+                },
+                serde_json::Value::Bool(b) => PropertyValue::Bool(*b),
+                serde_json::Value::Array(arr) => {
+                    if arr.len() == 3 {
+                        if let (Some(x), Some(y), Some(z)) = (
+                            arr[0].as_f64(),
+                            arr[1].as_f64(),
+                            arr[2].as_f64(),
+                        ) {
+                            PropertyValue::Vec3([x as f32, y as f32, z as f32])
+                        } else {
+                            continue;
+                        }
+                    } else {
+                        continue;
+                    }
+                },
+                _ => continue,
+            };
+            props.set_property(key.clone(), property_value);
+        }
+        
+        props
+    }
+}
+
+pub fn register_model_props_module(runtime: &mut Runtime) -> anyhow::Result<()> {
+    // Get property (generic)
+    runtime.register_function("getProperty", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("getProperty requires 2 arguments (properties, key)".to_string()));
+        }
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = props.properties.get(key)
+            .cloned()
+            .unwrap_or(serde_json::Value::Null);
+        
+        Ok(value)
+    })?;
+
+    // Set property (string)
+    runtime.register_function("setPropertyString", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 3 {
+            return Err(rustyscript::Error::Runtime("setPropertyString requires 3 arguments (properties, key, value)".to_string()));
+        }
+        
+        let mut props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = args[2].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Value must be a string".to_string()))?;
+        
+        props.properties.insert(key.to_string(), serde_json::Value::String(value.to_string()));
+        
+        serde_json::to_value(props)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize properties: {}", e)))
+    })?;
 
-pub fn register_model_props_module(engine: &mut Engine) {
-    engine.register_type_with_name::<ModelProperties>("Properties");
-
-    engine.register_fn("get_property", |props: &mut ModelProperties, key: &str| -> Dynamic {
-        match props.get_property(key) {
-            Some(PropertyValue::String(s)) => Dynamic::from(s.clone()),
-            Some(PropertyValue::Int(i)) => Dynamic::from(*i),
-            Some(PropertyValue::Float(f)) => Dynamic::from(*f),
-            Some(PropertyValue::Bool(b)) => Dynamic::from(*b),
-            Some(PropertyValue::Vec3(v)) => Dynamic::from([v[0] as f64, v[1] as f64, v[2] as f64]),
-            None => Dynamic::UNIT,
+    // Set property (int)
+    runtime.register_function("setPropertyInt", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 3 {
+            return Err(rustyscript::Error::Runtime("setPropertyInt requires 3 arguments (properties, key, value)".to_string()));
         }
-    });
-
-    engine.register_fn("set_property", |props: &mut ModelProperties, key: &str, value: String| {
-        props.set_property(key.to_string(), PropertyValue::String(value));
-    });
-
-    engine.register_fn("set_property", |props: &mut ModelProperties, key: &str, value: i64| {
-        props.set_property(key.to_string(), PropertyValue::Int(value));
-    });
-
-    engine.register_fn("set_property", |props: &mut ModelProperties, key: &str, value: f64| {
-        props.set_property(key.to_string(), PropertyValue::Float(value));
-    });
-
-    engine.register_fn("set_property", |props: &mut ModelProperties, key: &str, value: bool| {
-        props.set_property(key.to_string(), PropertyValue::Bool(value));
-    });
-
-    engine.register_fn("set_property", |props: &mut ModelProperties, key: &str, value: rhai::Array| {
-        if value.len() == 3 {
-            if let (Ok(x), Ok(y), Ok(z)) = (
-                value[0].as_float(),
-                value[1].as_float(), 
-                value[2].as_float()
-            ) {
-                props.set_property(key.to_string(), PropertyValue::Vec3([x as f32, y as f32, z as f32]));
-            }
+        
+        let mut props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = args[2].as_i64()
+            .ok_or_else(|| rustyscript::Error::Runtime("Value must be an integer".to_string()))?;
+        
+        props.properties.insert(key.to_string(), serde_json::Value::Number(serde_json::Number::from(value)));
+        
+        serde_json::to_value(props)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize properties: {}", e)))
+    })?;
+
+    // Set property (float)
+    runtime.register_function("setPropertyFloat", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 3 {
+            return Err(rustyscript::Error::Runtime("setPropertyFloat requires 3 arguments (properties, key, value)".to_string()));
         }
-    });
+        
+        let mut props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = args[2].as_f64()
+            .ok_or_else(|| rustyscript::Error::Runtime("Value must be a number".to_string()))?;
+        
+        props.properties.insert(key.to_string(), serde_json::Value::Number(
+            serde_json::Number::from_f64(value).unwrap_or(serde_json::Number::from(0))
+        ));
+        
+        serde_json::to_value(props)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize properties: {}", e)))
+    })?;
 
-    engine.register_fn("get_string", |props: &mut ModelProperties, key: &str| -> String {
-        match props.get_property(key) {
-            Some(PropertyValue::String(s)) => s.clone(),
-            _ => String::new(),
+    // Set property (bool)
+    runtime.register_function("setPropertyBool", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 3 {
+            return Err(rustyscript::Error::Runtime("setPropertyBool requires 3 arguments (properties, key, value)".to_string()));
         }
-    });
+        
+        let mut props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = args[2].as_bool()
+            .ok_or_else(|| rustyscript::Error::Runtime("Value must be a boolean".to_string()))?;
+        
+        props.properties.insert(key.to_string(), serde_json::Value::Bool(value));
+        
+        serde_json::to_value(props)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize properties: {}", e)))
+    })?;
 
-    engine.register_fn("get_int", |props: &mut ModelProperties, key: &str| -> i64 {
-        match props.get_property(key) {
-            Some(PropertyValue::Int(i)) => *i,
-            _ => 0,
+    // Set property (Vec3)
+    runtime.register_function("setPropertyVec3", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 3 {
+            return Err(rustyscript::Error::Runtime("setPropertyVec3 requires 3 arguments (properties, key, value)".to_string()));
+        }
+        
+        let mut props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = args[2].as_array()
+            .ok_or_else(|| rustyscript::Error::Runtime("Value must be an array".to_string()))?;
+        
+        if value.len() != 3 {
+            return Err(rustyscript::Error::Runtime("Vec3 array must have exactly 3 elements".to_string()));
         }
-    });
+        
+        let x = value[0].as_f64().ok_or_else(|| rustyscript::Error::Runtime("Vec3 elements must be numbers".to_string()))?;
+        let y = value[1].as_f64().ok_or_else(|| rustyscript::Error::Runtime("Vec3 elements must be numbers".to_string()))?;
+        let z = value[2].as_f64().ok_or_else(|| rustyscript::Error::Runtime("Vec3 elements must be numbers".to_string()))?;
+        
+        let vec3_array = serde_json::Value::Array(vec![
+            serde_json::Value::Number(serde_json::Number::from_f64(x).unwrap()),
+            serde_json::Value::Number(serde_json::Number::from_f64(y).unwrap()),
+            serde_json::Value::Number(serde_json::Number::from_f64(z).unwrap()),
+        ]);
+        
+        props.properties.insert(key.to_string(), vec3_array);
+        
+        serde_json::to_value(props)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize properties: {}", e)))
+    })?;
 
-    engine.register_fn("get_float", |props: &mut ModelProperties, key: &str| -> f64 {
-        match props.get_property(key) {
-            Some(PropertyValue::Float(f)) => *f,
-            _ => 0.0,
+    // Typed getters
+    runtime.register_function("getString", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("getString requires 2 arguments (properties, key)".to_string()));
         }
-    });
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = props.properties.get(key)
+            .and_then(|v| v.as_str())
+            .unwrap_or("")
+            .to_string();
+        
+        Ok(serde_json::Value::String(value))
+    })?;
 
-    engine.register_fn("get_bool", |props: &mut ModelProperties, key: &str| -> bool {
-        match props.get_property(key) {
-            Some(PropertyValue::Bool(b)) => *b,
-            _ => false,
+    runtime.register_function("getInt", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("getInt requires 2 arguments (properties, key)".to_string()));
         }
-    });
-
-    engine.register_fn("get_vec3", |props: &mut ModelProperties, key: &str| -> rhai::Array {
-        match props.get_property(key) {
-            Some(PropertyValue::Vec3(v)) => {
-                vec![Dynamic::from(v[0] as f64), Dynamic::from(v[1] as f64), Dynamic::from(v[2] as f64)]
-            },
-            _ => vec![Dynamic::from(0.0), Dynamic::from(0.0), Dynamic::from(0.0)],
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = props.properties.get(key)
+            .and_then(|v| v.as_i64())
+            .unwrap_or(0);
+        
+        Ok(serde_json::Value::Number(serde_json::Number::from(value)))
+    })?;
+
+    runtime.register_function("getFloat", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("getFloat requires 2 arguments (properties, key)".to_string()));
         }
-    });
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = props.properties.get(key)
+            .and_then(|v| v.as_f64())
+            .unwrap_or(0.0);
+        
+        Ok(serde_json::Value::Number(serde_json::Number::from_f64(value).unwrap()))
+    })?;
 
-    engine.register_fn("has_property", |props: &mut ModelProperties, key: &str| -> bool {
-        props.get_property(key).is_some()
-    });
+    runtime.register_function("getBool", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("getBool requires 2 arguments (properties, key)".to_string()));
+        }
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = props.properties.get(key)
+            .and_then(|v| v.as_bool())
+            .unwrap_or(false);
+        
+        Ok(serde_json::Value::Bool(value))
+    })?;
+
+    runtime.register_function("getVec3", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("getVec3 requires 2 arguments (properties, key)".to_string()));
+        }
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let value = props.properties.get(key)
+            .and_then(|v| v.as_array())
+            .cloned()
+            .unwrap_or_else(|| vec![
+                serde_json::Value::Number(serde_json::Number::from(0)),
+                serde_json::Value::Number(serde_json::Number::from(0)),
+                serde_json::Value::Number(serde_json::Number::from(0)),
+            ]);
+        
+        Ok(serde_json::Value::Array(value))
+    })?;
+
+    runtime.register_function("hasProperty", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("hasProperty requires 2 arguments (properties, key)".to_string()));
+        }
+        
+        let props: SerializableModelProperties = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid properties: {}", e)))?;
+        
+        let key = args[1].as_str()
+            .ok_or_else(|| rustyscript::Error::Runtime("Key must be a string".to_string()))?;
+        
+        let has_property = props.properties.contains_key(key);
+        Ok(serde_json::Value::Bool(has_property))
+    })?;
 
     log::info!("[Script] Initialised entity custom properties module");
+    Ok(())
 }
\ No newline at end of file
diff --git a/eucalyptus/src/scripting/input.rs b/eucalyptus/src/scripting/input.rs
index 3938c2d..f60b1bb 100644
--- a/eucalyptus/src/scripting/input.rs
+++ b/eucalyptus/src/scripting/input.rs
@@ -1,11 +1,14 @@
 use std::{collections::{HashMap, HashSet}, time::{Duration, Instant}};
 
-use rhai::*;
+use rustyscript::{serde_json, Runtime};
+use serde::{Deserialize, Serialize};
 use winit::{event::MouseButton, keyboard::KeyCode};
 
-#[derive(rhai::CustomType, Clone)]
+#[derive(Clone)]
 pub struct InputState {
+    #[allow(dead_code)]
     pub last_key_press_times: HashMap<KeyCode, Instant>,
+    #[allow(dead_code)]
     pub double_press_threshold: Duration,
     pub mouse_pos: (f64, f64),
     pub mouse_button: HashSet<MouseButton>,    
@@ -14,6 +17,28 @@ pub struct InputState {
     pub is_cursor_locked: bool,
 }
 
+// Create a serializable version for script communication
+#[derive(Clone, Serialize, Deserialize)]
+pub struct SerializableInputState {
+    pub mouse_pos: (f64, f64),
+    pub pressed_keys: Vec<String>, // Convert KeyCode to strings
+    pub mouse_delta: Option<(f64, f64)>,
+    pub is_cursor_locked: bool,
+}
+
+impl From<&InputState> for SerializableInputState {
+    fn from(input_state: &InputState) -> Self {
+        Self {
+            mouse_pos: input_state.mouse_pos,
+            pressed_keys: input_state.pressed_keys.iter()
+                .filter_map(|key| keycode_to_string(*key))
+                .collect(),
+            mouse_delta: input_state.mouse_delta,
+            is_cursor_locked: input_state.is_cursor_locked,
+        }
+    }
+}
+
 impl Default for InputState {
     fn default() -> Self {
         Self::new()
@@ -39,72 +64,224 @@ impl InputState {
         self.is_cursor_locked = toggle;
     }
 
-    pub fn register_input_modules(engine: &mut Engine) {
-        engine.register_type_with_name::<InputState>("InputState");
-        engine.register_type_with_name::<Key>("Key");
+    pub fn register_input_modules(runtime: &mut Runtime) -> anyhow::Result<()> {
+        // Register input functions that take SerializableInputState as parameter
+        runtime.register_function("isKeyPressed", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            if args.len() != 2 {
+                return Err(rustyscript::Error::Runtime("isKeyPressed requires 2 arguments (inputState, keyCode)".to_string()));
+            }
+            
+            let input_state: SerializableInputState = serde_json::from_value(args[0].clone())
+                .map_err(|e| rustyscript::Error::Runtime(format!("Invalid input state: {}", e)))?;
+            
+            let key_code_str = args[1].as_str()
+                .ok_or_else(|| rustyscript::Error::Runtime("Key code must be a string".to_string()))?;
+            
+            let is_pressed = input_state.pressed_keys.contains(&key_code_str.to_string());
+            Ok(serde_json::Value::Bool(is_pressed))
+        })?;
 
-        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("mouse_dx", |s: &mut InputState| s.mouse_delta.map(|d| d.0).unwrap_or(0.0))
-            .register_get("mouse_dy", |s: &mut InputState| s.mouse_delta.map(|d| d.1).unwrap_or(0.0))
-            .register_fn("lock_cursor", |s: &mut InputState, value: bool| s.is_cursor_locked = value);
+        runtime.register_function("getMouseX", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            if args.len() != 1 {
+                return Err(rustyscript::Error::Runtime("getMouseX requires 1 argument (inputState)".to_string()));
+            }
+            
+            let input_state: SerializableInputState = serde_json::from_value(args[0].clone())
+                .map_err(|e| rustyscript::Error::Runtime(format!("Invalid input state: {}", e)))?;
+            
+            Ok(serde_json::Value::Number(serde_json::Number::from_f64(input_state.mouse_pos.0).unwrap()))
+        })?;
 
-        let mut kmod = Module::new();
+        runtime.register_function("getMouseY", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            if args.len() != 1 {
+                return Err(rustyscript::Error::Runtime("getMouseY requires 1 argument (inputState)".to_string()));
+            }
+            
+            let input_state: SerializableInputState = serde_json::from_value(args[0].clone())
+                .map_err(|e| rustyscript::Error::Runtime(format!("Invalid input state: {}", e)))?;
+            
+            Ok(serde_json::Value::Number(serde_json::Number::from_f64(input_state.mouse_pos.1).unwrap()))
+        })?;
 
-        macro_rules! add_key {
-            ($name:ident, $kc:expr) => {
-                kmod.set_var(stringify!($name), Key($kc));
-            };
-        }
+        runtime.register_function("getMouseDeltaX", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            if args.len() != 1 {
+                return Err(rustyscript::Error::Runtime("getMouseDeltaX requires 1 argument (inputState)".to_string()));
+            }
+            
+            let input_state: SerializableInputState = serde_json::from_value(args[0].clone())
+                .map_err(|e| rustyscript::Error::Runtime(format!("Invalid input state: {}", e)))?;
+            
+            let delta_x = input_state.mouse_delta.map(|d| d.0).unwrap_or(0.0);
+            Ok(serde_json::Value::Number(serde_json::Number::from_f64(delta_x).unwrap()))
+        })?;
+
+        runtime.register_function("getMouseDeltaY", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            if args.len() != 1 {
+                return Err(rustyscript::Error::Runtime("getMouseDeltaY requires 1 argument (inputState)".to_string()));
+            }
+            
+            let input_state: SerializableInputState = serde_json::from_value(args[0].clone())
+                .map_err(|e| rustyscript::Error::Runtime(format!("Invalid input state: {}", e)))?;
+            
+            let delta_y = input_state.mouse_delta.map(|d| d.1).unwrap_or(0.0);
+            Ok(serde_json::Value::Number(serde_json::Number::from_f64(delta_y).unwrap()))
+        })?;
+
+        runtime.register_function("lockCursor", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            if args.len() != 1 {
+                return Err(rustyscript::Error::Runtime("lockCursor requires 1 argument (locked)".to_string()));
+            }
+            
+            let locked = args[0].as_bool()
+                .ok_or_else(|| rustyscript::Error::Runtime("Locked parameter must be a boolean".to_string()))?;
+            
+            // This function would need to communicate back to the engine to actually lock the cursor
+            // For now, just return success
+            Ok(serde_json::Value::Bool(true))
+        })?;
 
-        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());
         log::info!("[Script] Initialised input module");
+        Ok(())
+    }
+}
+
+// Helper function to convert string to KeyCode
+fn string_to_keycode(key_str: &str) -> Option<KeyCode> {
+    match key_str {
+        "KeyA" => Some(KeyCode::KeyA),
+        "KeyB" => Some(KeyCode::KeyB),
+        "KeyC" => Some(KeyCode::KeyC),
+        "KeyD" => Some(KeyCode::KeyD),
+        "KeyE" => Some(KeyCode::KeyE),
+        "KeyF" => Some(KeyCode::KeyF),
+        "KeyG" => Some(KeyCode::KeyG),
+        "KeyH" => Some(KeyCode::KeyH),
+        "KeyI" => Some(KeyCode::KeyI),
+        "KeyJ" => Some(KeyCode::KeyJ),
+        "KeyK" => Some(KeyCode::KeyK),
+        "KeyL" => Some(KeyCode::KeyL),
+        "KeyM" => Some(KeyCode::KeyM),
+        "KeyN" => Some(KeyCode::KeyN),
+        "KeyO" => Some(KeyCode::KeyO),
+        "KeyP" => Some(KeyCode::KeyP),
+        "KeyQ" => Some(KeyCode::KeyQ),
+        "KeyR" => Some(KeyCode::KeyR),
+        "KeyS" => Some(KeyCode::KeyS),
+        "KeyT" => Some(KeyCode::KeyT),
+        "KeyU" => Some(KeyCode::KeyU),
+        "KeyV" => Some(KeyCode::KeyV),
+        "KeyW" => Some(KeyCode::KeyW),
+        "KeyX" => Some(KeyCode::KeyX),
+        "KeyY" => Some(KeyCode::KeyY),
+        "KeyZ" => Some(KeyCode::KeyZ),
+        "Space" => Some(KeyCode::Space),
+        "ShiftLeft" => Some(KeyCode::ShiftLeft),
+        "ShiftRight" => Some(KeyCode::ShiftRight),
+        "ControlLeft" => Some(KeyCode::ControlLeft),
+        "ControlRight" => Some(KeyCode::ControlRight),
+        "AltLeft" => Some(KeyCode::AltLeft),
+        "AltRight" => Some(KeyCode::AltRight),
+        "Escape" => Some(KeyCode::Escape),
+        "Enter" => Some(KeyCode::Enter),
+        "Tab" => Some(KeyCode::Tab),
+        "ArrowUp" => Some(KeyCode::ArrowUp),
+        "ArrowDown" => Some(KeyCode::ArrowDown),
+        "ArrowLeft" => Some(KeyCode::ArrowLeft),
+        "ArrowRight" => Some(KeyCode::ArrowRight),
+        "Digit0" => Some(KeyCode::Digit0),
+        "Digit1" => Some(KeyCode::Digit1),
+        "Digit2" => Some(KeyCode::Digit2),
+        "Digit3" => Some(KeyCode::Digit3),
+        "Digit4" => Some(KeyCode::Digit4),
+        "Digit5" => Some(KeyCode::Digit5),
+        "Digit6" => Some(KeyCode::Digit6),
+        "Digit7" => Some(KeyCode::Digit7),
+        "Digit8" => Some(KeyCode::Digit8),
+        "Digit9" => Some(KeyCode::Digit9),
+        "F1" => Some(KeyCode::F1),
+        "F2" => Some(KeyCode::F2),
+        "F3" => Some(KeyCode::F3),
+        "F4" => Some(KeyCode::F4),
+        "F5" => Some(KeyCode::F5),
+        "F6" => Some(KeyCode::F6),
+        "F7" => Some(KeyCode::F7),
+        "F8" => Some(KeyCode::F8),
+        "F9" => Some(KeyCode::F9),
+        "F10" => Some(KeyCode::F10),
+        "F11" => Some(KeyCode::F11),
+        "F12" => Some(KeyCode::F12),
+        _ => None,
+    }
+}
+
+// Helper function to convert KeyCode to string
+fn keycode_to_string(key_code: KeyCode) -> Option<String> {
+    match key_code {
+        KeyCode::KeyA => Some("KeyA".to_string()),
+        KeyCode::KeyB => Some("KeyB".to_string()),
+        KeyCode::KeyC => Some("KeyC".to_string()),
+        KeyCode::KeyD => Some("KeyD".to_string()),
+        KeyCode::KeyE => Some("KeyE".to_string()),
+        KeyCode::KeyF => Some("KeyF".to_string()),
+        KeyCode::KeyG => Some("KeyG".to_string()),
+        KeyCode::KeyH => Some("KeyH".to_string()),
+        KeyCode::KeyI => Some("KeyI".to_string()),
+        KeyCode::KeyJ => Some("KeyJ".to_string()),
+        KeyCode::KeyK => Some("KeyK".to_string()),
+        KeyCode::KeyL => Some("KeyL".to_string()),
+        KeyCode::KeyM => Some("KeyM".to_string()),
+        KeyCode::KeyN => Some("KeyN".to_string()),
+        KeyCode::KeyO => Some("KeyO".to_string()),
+        KeyCode::KeyP => Some("KeyP".to_string()),
+        KeyCode::KeyQ => Some("KeyQ".to_string()),
+        KeyCode::KeyR => Some("KeyR".to_string()),
+        KeyCode::KeyS => Some("KeyS".to_string()),
+        KeyCode::KeyT => Some("KeyT".to_string()),
+        KeyCode::KeyU => Some("KeyU".to_string()),
+        KeyCode::KeyV => Some("KeyV".to_string()),
+        KeyCode::KeyW => Some("KeyW".to_string()),
+        KeyCode::KeyX => Some("KeyX".to_string()),
+        KeyCode::KeyY => Some("KeyY".to_string()),
+        KeyCode::KeyZ => Some("KeyZ".to_string()),
+        KeyCode::Space => Some("Space".to_string()),
+        KeyCode::ShiftLeft => Some("ShiftLeft".to_string()),
+        KeyCode::ShiftRight => Some("ShiftRight".to_string()),
+        KeyCode::ControlLeft => Some("ControlLeft".to_string()),
+        KeyCode::ControlRight => Some("ControlRight".to_string()),
+        KeyCode::AltLeft => Some("AltLeft".to_string()),
+        KeyCode::AltRight => Some("AltRight".to_string()),
+        KeyCode::Escape => Some("Escape".to_string()),
+        KeyCode::Enter => Some("Enter".to_string()),
+        KeyCode::Tab => Some("Tab".to_string()),
+        KeyCode::ArrowUp => Some("ArrowUp".to_string()),
+        KeyCode::ArrowDown => Some("ArrowDown".to_string()),
+        KeyCode::ArrowLeft => Some("ArrowLeft".to_string()),
+        KeyCode::ArrowRight => Some("ArrowRight".to_string()),
+        KeyCode::Digit0 => Some("Digit0".to_string()),
+        KeyCode::Digit1 => Some("Digit1".to_string()),
+        KeyCode::Digit2 => Some("Digit2".to_string()),
+        KeyCode::Digit3 => Some("Digit3".to_string()),
+        KeyCode::Digit4 => Some("Digit4".to_string()),
+        KeyCode::Digit5 => Some("Digit5".to_string()),
+        KeyCode::Digit6 => Some("Digit6".to_string()),
+        KeyCode::Digit7 => Some("Digit7".to_string()),
+        KeyCode::Digit8 => Some("Digit8".to_string()),
+        KeyCode::Digit9 => Some("Digit9".to_string()),
+        KeyCode::F1 => Some("F1".to_string()),
+        KeyCode::F2 => Some("F2".to_string()),
+        KeyCode::F3 => Some("F3".to_string()),
+        KeyCode::F4 => Some("F4".to_string()),
+        KeyCode::F5 => Some("F5".to_string()),
+        KeyCode::F6 => Some("F6".to_string()),
+        KeyCode::F7 => Some("F7".to_string()),
+        KeyCode::F8 => Some("F8".to_string()),
+        KeyCode::F9 => Some("F9".to_string()),
+        KeyCode::F10 => Some("F10".to_string()),
+        KeyCode::F11 => Some("F11".to_string()),
+        KeyCode::F12 => Some("F12".to_string()),
+        _ => None,
     }
 }
 
-#[derive(rhai::CustomType, Clone, Copy)]
+#[derive(Clone, Copy)]
 pub struct Key(pub KeyCode);
\ No newline at end of file
diff --git a/eucalyptus/src/scripting/math.rs b/eucalyptus/src/scripting/math.rs
index b600ccf..150477e 100644
--- a/eucalyptus/src/scripting/math.rs
+++ b/eucalyptus/src/scripting/math.rs
@@ -1,8 +1,166 @@
-use rustyscript::{Module, Runtime};
+use dropbear_engine::entity::Transform;
+use glam::{DQuat, DVec3};
+use rustyscript::{serde_json, Runtime};
 
 pub fn register_math_functions(runtime: &mut Runtime) -> anyhow::Result<()> {
-    // runtime.load_module_async(module)
-    let module = Module::load_dir("typescript")?;
-    runtime.load_modules(module, side_modules)
+    
+    // Transform functions
+    runtime.register_function("createTransform", |_args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        let transform = Transform::new();
+        serde_json::to_value(transform)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Transform: {}", e)))
+    })?;
+
+    runtime.register_function("transformTranslate", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("transformTranslate requires 2 arguments".to_string()));
+        }
+        
+        let mut transform: Transform = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid transform: {}", e)))?;
+        
+        // Handle both array and individual values for translation
+        let translation = if let Some(array) = args[1].as_array() {
+            if array.len() != 3 {
+                return Err(rustyscript::Error::Runtime("Translation array must have 3 elements".to_string()));
+            }
+            DVec3::new(
+                array[0].as_f64().unwrap_or(0.0),
+                array[1].as_f64().unwrap_or(0.0),
+                array[2].as_f64().unwrap_or(0.0),
+            )
+        } else {
+            return Err(rustyscript::Error::Runtime("Translation must be an array".to_string()));
+        };
+        
+        transform.position += translation;
+        serde_json::to_value(transform)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Transform: {}", e)))
+    })?;
+
+    runtime.register_function("transformRotateX", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("transformRotateX requires 2 arguments".to_string()));
+        }
+        
+        let mut transform: Transform = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid transform: {}", e)))?;
+        
+        let angle = args[1].as_f64().unwrap_or(0.0);
+        let rotation = DQuat::from_rotation_x(angle);
+        transform.rotation = rotation * transform.rotation;
+        
+        serde_json::to_value(transform)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Transform: {}", e)))
+    })?;
+
+    runtime.register_function("transformRotateY", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("transformRotateY requires 2 arguments".to_string()));
+        }
+        
+        let mut transform: Transform = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid transform: {}", e)))?;
+        
+        let angle = args[1].as_f64().unwrap_or(0.0);
+        let rotation = DQuat::from_rotation_y(angle);
+        transform.rotation = rotation * transform.rotation;
+        
+        serde_json::to_value(transform)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Transform: {}", e)))
+    })?;
+
+    runtime.register_function("transformRotateZ", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("transformRotateZ requires 2 arguments".to_string()));
+        }
+        
+        let mut transform: Transform = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid transform: {}", e)))?;
+        
+        let angle = args[1].as_f64().unwrap_or(0.0);
+        let rotation = DQuat::from_rotation_z(angle);
+        transform.rotation = rotation * transform.rotation;
+        
+        serde_json::to_value(transform)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Transform: {}", e)))
+    })?;
+
+    runtime.register_function("transformScale", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 2 {
+            return Err(rustyscript::Error::Runtime("transformScale requires 2 arguments".to_string()));
+        }
+        
+        let mut transform: Transform = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid transform: {}", e)))?;
+        
+        let scale = if let Some(num) = args[1].as_f64() {
+            DVec3::splat(num)
+        } else if let Some(array) = args[1].as_array() {
+            if array.len() != 3 {
+                return Err(rustyscript::Error::Runtime("Scale array must have 3 elements".to_string()));
+            }
+            DVec3::new(
+                array[0].as_f64().unwrap_or(1.0),
+                array[1].as_f64().unwrap_or(1.0),
+                array[2].as_f64().unwrap_or(1.0),
+            )
+        } else {
+            return Err(rustyscript::Error::Runtime("Scale must be a number or array".to_string()));
+        };
+        
+        transform.scale *= scale;
+        serde_json::to_value(transform)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Transform: {}", e)))
+    })?;
+
+    runtime.register_function("transformMatrix", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 1 {
+            return Err(rustyscript::Error::Runtime("transformMatrix requires 1 argument".to_string()));
+        }
+        
+        let transform: Transform = serde_json::from_value(args[0].clone())
+            .map_err(|e| rustyscript::Error::Runtime(format!("Invalid transform: {}", e)))?;
+        
+        // Assuming Transform has a matrix() method that returns a 4x4 matrix
+        // You may need to adjust this based on your actual Transform implementation
+        let matrix = transform.matrix();
+        serde_json::to_value(matrix)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize matrix: {}", e)))
+    })?;
+
+    // Vec3 functions
+    runtime.register_function("createVec3", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        let x = args.get(0).and_then(|v| v.as_f64()).unwrap_or(0.0);
+        let y = args.get(1).and_then(|v| v.as_f64()).unwrap_or(0.0);
+        let z = args.get(2).and_then(|v| v.as_f64()).unwrap_or(0.0);
+        
+        let vec3 = DVec3::new(x, y, z);
+        serde_json::to_value(vec3)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Vec3: {}", e)))
+    })?;
+
+    // Quaternion functions
+    runtime.register_function("createQuatIdentity", |_args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        let quat = DQuat::IDENTITY;
+        serde_json::to_value(quat)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Quaternion: {}", e)))
+    })?;
+
+    runtime.register_function("createQuatFromEuler", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+        if args.len() != 3 {
+            return Err(rustyscript::Error::Runtime("createQuatFromEuler requires 3 arguments".to_string()));
+        }
+        
+        let x = args[0].as_f64().unwrap_or(0.0);
+        let y = args[1].as_f64().unwrap_or(0.0);
+        let z = args[2].as_f64().unwrap_or(0.0);
+        
+        let quat = DQuat::from_euler(glam::EulerRot::XYZ, x, y, z);
+        serde_json::to_value(quat)
+            .map_err(|e| rustyscript::Error::Runtime(format!("Failed to serialize Quaternion: {}", e)))
+    })?;
+
     log::info!("[Script] Initialised math module");
+    Ok(())
 }
\ No newline at end of file
diff --git a/eucalyptus/src/scripting/mod.rs b/eucalyptus/src/scripting/mod.rs
index 61c51ad..9a5a8f5 100644
--- a/eucalyptus/src/scripting/mod.rs
+++ b/eucalyptus/src/scripting/mod.rs
@@ -2,13 +2,9 @@ pub mod entity;
 pub mod math;
 pub mod input;
 
-use chrono::DateTime;
 use dropbear_engine::entity::{AdoptedEntity, Transform};
-use glam::{DQuat, DVec3};
 use hecs::World;
-use rustyscript::{Runtime, RuntimeOptions};
-use specta::{Type, TypeCollection};
-use specta_typescript::Typescript;
+use rustyscript::{serde_json, Module, ModuleHandle, Runtime, RuntimeOptions};
 use std::path::PathBuf;
 use std::{collections::HashMap, fs};
 
@@ -159,120 +155,43 @@ pub fn attach_script_to_entity(
     Ok(())
 }
 
-// #[cfg(feature = "editor")]
-// pub fn generate_ts() -> anyhow::Result<()> {
-//     let mut types = TypeCollection::default();
-//     types.register::<Transform>();
-
-//     let utc: DateTime<Utc> = Utc::now();
-//     let header = format!("
-// // @generated by dropbear-engine on {} UTC time
-// // Hello there! This is the generated stubs for the Eucalyptus Editor scripting component. 
-// // This file purely exists for the LSP and for TypeScripts type safety. Running this library 
-// // most likely won't do anything, but go ahead and be my guest. 
-// // EDIT THIS LIBRARY AT YOUR OWN WILL. IT IS RECOMMENDED TO NOT TOUCH THIS FILE
-//     ", utc);
-    
-//     if let Ok(project) = PROJECT.read() {
-//         Typescript::new().header(header).export_to(project.project_path.clone().join("src/dropbear-engine.d.ts"), types)?;
-//     } else {
-//         anyhow::bail!("Unable to retain Project Config lock, could not generate typescript bindings");
-//     }
-//     Ok(())
-// }
-
 pub struct ScriptManager {
     pub runtime: Runtime,
-    compiled_scripts: HashMap<String, AST>,
-    script_scopes: HashMap<hecs::Entity, Scope<'static>>,
+    compiled_scripts: HashMap<String, ModuleHandle>,
+    entity_script_data: HashMap<hecs::Entity, serde_json::Value>,
 }
 
 impl ScriptManager {
     pub fn new() -> anyhow::Result<Self> {
         let mut runtime = Runtime::new(RuntimeOptions::default())?;
-        
 
-        // // REGISTER FUNCTIONS HERE
-        // math::register_math_functions(&mut runtime);
-        // input::InputState::register_input_modules(&mut runtime);
-        // entity::register_model_props_module(&mut runtime);
-
-        // // register types used by scripts
-        // runtime.register_type_with_name::<Transform>("Transform");
-        // runtime.register_type_with_name::<DQuat>("Quaternion");
-        // runtime.register_type_with_name::<DVec3>("Vector3");
-
-        // runtime.mod
-
-        // // constructors / helpers (alias names to match helper.ts)
-        // runtime.register_function("createTransform", Transform::new);
-        // runtime.register_function("createVec3", |x: f64, y: f64, z: f64| DVec3::new(x, y, z));
-        // // keep the original alias if you used "vec3" elsewhere
-        // runtime.register_function("vec3", |x: f64, y: f64, z: f64| DVec3::new(x, y, z));
-
-        // runtime.register_function("createQuatIdentity", || DQuat::IDENTITY);
-        // // from-euler: adapt to your exact DQuat API if different
-        // runtime.register_function("createQuatFromEuler", |x: f64, y: f64, z: f64| {
-        //     // uses EulerRot::XYZ — adjust if your glam API differs
-        //     DQuat::from_euler(glam::EulerRot::XYZ, x, y, z)
-        // });
-
-        // // transform helpers: simple implementations that return an updated Transform
-        // runtime.register_function(
-        //     "transformTranslate",
-        //     |mut t: Transform, v: DVec3| {
-        //         t.position += v;
-        //         t
-        //     },
-        // );
-        // runtime.register_function(
-        //     "transformScale",
-        //     |mut t: Transform, s: DVec3| {
-        //         t.scale *= s;
-        //         t
-        //     },
-        // );
-        // runtime.register_function(
-        //     "transformMatrix",
-        //     |t: Transform| {
-        //         // return whatever representation your scripts expect (e.g. a 4x4 matrix type)
-        //         t.matrix() // replace with actual method if different
-        //     },
-        // );
-
-        // // axis rotations — simple wrappers that multiply quaternions
-        // runtime.register_function("transformRotateX", |mut t: Transform, a: f64| {
-        //     let r = DQuat::from_rotation_x(a);
-        //     t.rotation = r * t.rotation;
-        //     t
-        // });
-        // runtime.register_function("transformRotateY", |mut t: Transform, a: f64| {
-        //     let r = DQuat::from_rotation_y(a);
-        //     t.rotation = r * t.rotation;
-        //     t
-        // });
-        // runtime.register_function("transformRotateZ", |mut t: Transform, a: f64| {
-        //     let r = DQuat::from_rotation_z(a);
-        //     t.rotation = r * t.rotation;
-        //     t
-        // });
-
-        // // utils already in your file — log and time
-        // runtime.register_function("log", |msg: String| {
-        //     println!("[Script] {}", msg);
-        // });
-        // runtime.register_function("time", || {
-        //     std::time::SystemTime::now()
-        //         .duration_since(std::time::UNIX_EPOCH)
-        //         .unwrap()
-        //         .as_secs_f64()
-        // });
-
-        Self {
+        // Register modules
+        math::register_math_functions(&mut runtime)?;
+        input::InputState::register_input_modules(&mut runtime)?;
+        entity::register_model_props_module(&mut runtime)?;
+
+        // Register utility functions
+        runtime.register_function("log", |args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            let msg = args.get(0)
+                .and_then(|v| v.as_str())
+                .unwrap_or("undefined");
+            println!("[Script] {}", msg);
+            Ok(serde_json::Value::Null)
+        })?;
+
+        runtime.register_function("time", |_args: &[serde_json::Value]| -> Result<serde_json::Value, rustyscript::Error> {
+            let time = std::time::SystemTime::now()
+                .duration_since(std::time::UNIX_EPOCH)
+                .unwrap()
+                .as_secs_f64();
+            Ok(serde_json::Value::Number(serde_json::Number::from_f64(time).unwrap()))
+        })?;
+
+        Ok(Self {
             runtime,
             compiled_scripts: HashMap::new(),
-            script_scopes: HashMap::new(),
-        }
+            entity_script_data: HashMap::new(),
+        })
     }
 
     pub fn init_entity_script(
@@ -283,8 +202,8 @@ impl ScriptManager {
         input_state: &input::InputState,
     ) -> anyhow::Result<()> {
         if let Ok(mut q) = world.query_one::<&AdoptedEntity>(entity_id) {
-                if let Some(adopted) = q.get() {
-                    log_once::debug_once!(
+            if let Some(adopted) = q.get() {
+                log_once::debug_once!(
                     "init_entity_script: '{}' for entity {:?} -> label='{}' path='{}'",
                     script_name,
                     entity_id,
@@ -296,42 +215,43 @@ impl ScriptManager {
             log_once::debug_once!("init_entity_script: '{}' for entity {:?}", script_name, entity_id);
         }
 
-        if let Some(ast) = self.compiled_scripts.get(script_name) {
-            let mut scope = Scope::new();
+        if let Some(module) = self.compiled_scripts.get(script_name) {
+            // Prepare script data
+            let mut script_data = serde_json::Map::new();
 
-            if let Ok(transform) = world.query_one_mut::<&mut Transform>(entity_id) {
-                scope.push("transform", *transform);
+            // Add transform data
+            if let Ok(mut transform_query) = world.query_one::<&Transform>(entity_id) {
+                if let Some(transform) = transform_query.get() {
+                    script_data.insert("transform".to_string(), serde_json::to_value(transform)?);
+                }
             }
 
-            if let Ok(properties) = world.query_one_mut::<&mut ModelProperties>(entity_id) {
-                scope.push("entity", properties.clone());
+            // Add entity properties
+            if let Ok(mut properties_query) = world.query_one::<&ModelProperties>(entity_id) {
+                if let Some(properties) = properties_query.get() {
+                    script_data.insert("entity".to_string(), serde_json::to_value(properties)?);
+                } else {
+                    let default_props = ModelProperties::default();
+                    script_data.insert("entity".to_string(), serde_json::to_value(&default_props)?);
+                }
             } else {
                 let default_props = ModelProperties::default();
-                // default_props.set_property(String::from("speed"), PropertyValue::Float(1.0));
-                scope.push("entity", default_props);
+                script_data.insert("entity".to_string(), serde_json::to_value(&default_props)?);
             }
 
-            scope.push("input", input_state.clone());
+            // Add input state
+            let serializable_input = input::SerializableInputState::from(input_state);
+            script_data.insert("input".to_string(), serde_json::to_value(&serializable_input)?);
 
-            if let Ok(_) = self.engine.call_fn::<()>(&mut scope, ast, "init", ()) {
+            // Call init function if it exists - specify return type
+            let args: Vec<serde_json::Value> = Vec::new();
+            if let Ok(_result) = self.runtime.call_function::<serde_json::Value>(Some(module), "load", &args) {
                 log::debug!("Called init for entity {:?}", entity_id);
-
-                if let Some(properties_from_scope) = scope.get_value::<ModelProperties>("entity") {
-                    if let Ok(properties) = world.query_one_mut::<&mut ModelProperties>(entity_id) {
-                        *properties = properties_from_scope;
-                    } else {
-                        let _ = world.insert_one(entity_id, properties_from_scope);
-                    }
-                }
-
-                if let Some(transform_from_scope) = scope.get_value::<Transform>("transform") {
-                    if let Ok(transform) = world.query_one_mut::<&mut Transform>(entity_id) {
-                        *transform = transform_from_scope;
-                    }
-                }
             }
 
-            self.script_scopes.insert(entity_id, scope);
+            // Store script data for this entity
+            self.entity_script_data.insert(entity_id, serde_json::Value::Object(script_data));
+
             Ok(())
         } else {
             Err(anyhow::anyhow!("Script '{}' not found", script_name))
@@ -346,41 +266,44 @@ impl ScriptManager {
         input_state: &input::InputState,
         dt: f32,
     ) -> anyhow::Result<()> {
-        if let Some(ast) = self.compiled_scripts.get(script_name) {
-            if let Some(scope) = self.script_scopes.get_mut(&entity_id) {
-                if let Ok(transform) = world.query_one_mut::<&mut Transform>(entity_id) {
-                    scope.set_value("transform", *transform);
+        if let Some(module) = self.compiled_scripts.get(script_name) {
+            // Update script data
+            let mut script_data = serde_json::Map::new();
+
+            // Update transform data
+            if let Ok(mut transform_query) = world.query_one::<&Transform>(entity_id) {
+                if let Some(transform) = transform_query.get() {
+                    script_data.insert("transform".to_string(), serde_json::to_value(transform)?);
                 }
+            }
 
-                if let Ok(mut properties_query) = world.query_one::<&ModelProperties>(entity_id) {
-                    if let Some(properties) = properties_query.get() {
-                        scope.set_value("entity", properties.clone());
-                    }
+            // Update entity properties
+            if let Ok(mut properties_query) = world.query_one::<&ModelProperties>(entity_id) {
+                if let Some(properties) = properties_query.get() {
+                    script_data.insert("entity".to_string(), serde_json::to_value(properties)?);
                 }
+            }
 
-                scope.set_value("input", input_state.clone());
-
-                match self.engine.call_fn::<()>(scope, ast, "update", (dt,)) {
-                    Ok(_) => {
-                        if let Some(transform_from_scope) = scope.get_value::<Transform>("transform") {
-                            if let Ok(transform) = world.query_one_mut::<&mut Transform>(entity_id) {
-                                *transform = transform_from_scope;
-                            }
-                        }
-
-                        if let Some(properties_from_scope) = scope.get_value::<ModelProperties>("entity") {
-                            if let Ok(properties) = world.query_one_mut::<&mut ModelProperties>(entity_id) {
-                                *properties = properties_from_scope;
-                            }
-                        }
-                    }
-                    Err(e) => {
-                        log_once::error_once!("Script execution error for entity {:?}: {}", entity_id, e);
-                    }
+            // Update input state
+            let serializable_input = input::SerializableInputState::from(input_state);
+            script_data.insert("input".to_string(), serde_json::to_value(&serializable_input)?);
+
+            // Call update function if it exists - specify return type and fix parameter passing
+            let dt_value = serde_json::Value::Number(serde_json::Number::from_f64(dt as f64).unwrap());
+            match self.runtime.call_function::<serde_json::Value>(Some(module), "update", &vec![dt_value]) {
+                Ok(_result) => {
+                    log::trace!("Called update for entity {:?}", entity_id);
+                    // Here you would need to extract any modified data from the script
+                    // and update the world accordingly. This depends on how rustyscript
+                    // handles data exchange between Rust and JS.
+                }
+                Err(e) => {
+                    log_once::error_once!("Script execution error for entity {:?}: {}", entity_id, e);
                 }
-            } else {
-                log_once::error_once!("Unable to get scope of entity {:?}", entity_id);
             }
+
+            // Update stored script data
+            self.entity_script_data.insert(entity_id, serde_json::Value::Object(script_data));
         } else {
             log_once::error_once!("Unable to fetch compiled scripts for entity {:?}. Script Name: {}", entity_id, script_name);
         }
@@ -388,14 +311,19 @@ impl ScriptManager {
     }
 
     pub fn load_script_from_source(&mut self, script_name: &String, script_content: &String) -> anyhow::Result<String> {
-        let ast = self.engine.compile(&script_content).map_err(|e| {
-            log::error!("Compiling AST for script [{}] returned an error: {}", script_name, e);
-            e
-        })?;
-        self.compiled_scripts.insert(script_name.clone(), ast);
-
-        log::info!("Loaded script: {}", script_name);
-        Ok(script_name.to_string())
+        let module = Module::new(&script_name, script_content);
+        
+        match self.runtime.load_module(&module) {
+            Ok(module) => {
+                self.compiled_scripts.insert(script_name.clone(), module);
+                log::info!("Loaded script: {}", script_name);
+                Ok(script_name.to_string())
+            }
+            Err(e) => {
+                log::error!("Compiling module for script [{}] returned an error: {}", script_name, e);
+                Err(e.into())
+            }
+        }
     }
 
     pub fn load_script(&mut self, script_path: &PathBuf) -> anyhow::Result<String> {
@@ -406,31 +334,22 @@ impl ScriptManager {
             .to_string_lossy()
             .to_string();
 
-        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);
-        Ok(script_name)
+        let module = Module::new(&script_name, &script_content);
+        
+        match self.runtime.load_module(&module) {
+            Ok(module) => {
+                self.compiled_scripts.insert(script_name.clone(), module);
+                log::info!("Loaded script: {}", script_name);
+                Ok(script_name)
+            }
+            Err(e) => {
+                log::error!("Compiling module for script path [{}] returned an error: {}", script_path.display(), e);
+                Err(e.into())
+            }
+        }
     }
 
     pub fn remove_entity_script(&mut self, entity_id: hecs::Entity) {
-        self.script_scopes.remove(&entity_id);
+        self.entity_script_data.remove(&entity_id);
     }
-
-    // maybe useful later???
-    // pub fn reload_script(
-    //     &mut self,
-    //     script_name: &str,
-    //     script_path: &PathBuf,
-    // ) -> anyhow::Result<()> {
-    //     let script_content = fs::read_to_string(script_path)?;
-    //     let ast = self.engine.compile(&script_content)?;
-    //     self.compiled_scripts.insert(script_name.to_string(), ast);
-
-    //     log::info!("Reloaded script: {}", script_name);
-    //     Ok(())
-    // }
 }
\ No newline at end of file
diff --git a/eucalyptus/src/states.rs b/eucalyptus/src/states.rs
index dc52497..07c39f4 100644
--- a/eucalyptus/src/states.rs
+++ b/eucalyptus/src/states.rs
@@ -31,7 +31,6 @@ use serde::{Deserialize, Serialize};
 use dropbear_engine::model::Model;
 use dropbear_engine::starter::plane::PlaneBuilder;
 use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
-use crate::build::build;
 use crate::camera::CameraType;
 #[cfg(feature = "editor")]
 use crate::editor::EditorTab;
diff --git a/eucalyptus/src/template.ts b/eucalyptus/src/template.ts
index 8826b52..4670e21 100644
--- a/eucalyptus/src/template.ts
+++ b/eucalyptus/src/template.ts
@@ -1 +1,10 @@
-// todo
\ No newline at end of file
+// dropbear-engine script template for eucalyptus
+import * as Dropbear from "./dropbear";
+
+export function onLoad() {
+
+}
+
+export function onUpdate(dt: Number) {
+    
+}
diff --git a/eucalyptus/src/typescript/helper.ts b/eucalyptus/src/typescript/helper.ts
deleted file mode 100644
index a49aca5..0000000
--- a/eucalyptus/src/typescript/helper.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-// Modules for the dropbear-engine scripting component
-
-type TransformT = any;
-type Vec3T = any;
-type QuatT = any;
-
-function hostFn(name: string) {
-    const fn = (globalThis as any)[name];
-    if (typeof fn !== "function") {
-        throw new Error(`Host function ${name}() is not available. Make sure the runtime exposes it.`);
-    }
-    return fn;
-}
-
-const Transform = {
-    create: (): TransformT => hostFn("createTransform")(),
-    translate: (transform: TransformT, translation: [number, number, number] | number[]): TransformT =>
-        hostFn("transformTranslate")(transform, translation),
-    rotateX: (transform: TransformT, angle: number): TransformT =>
-        hostFn("transformRotateX")(transform, angle),
-    rotateY: (transform: TransformT, angle: number): TransformT =>
-        hostFn("transformRotateY")(transform, angle),
-    rotateZ: (transform: TransformT, angle: number): TransformT =>
-        hostFn("transformRotateZ")(transform, angle),
-    scale: (transform: TransformT, scale: number | [number, number, number]): TransformT =>
-        hostFn("transformScale")(transform, scale),
-    matrix: (transform: TransformT): TransformT => hostFn("transformMatrix")(transform),
-};
-
-const Vec3 = {
-    create: (x = 0, y = 0, z = 0): Vec3T => hostFn("createVec3")(x, y, z),
-    zero: (): Vec3T => hostFn("createVec3")(0, 0, 0),
-    one: (): Vec3T => hostFn("createVec3")(1, 1, 1),
-};
-
-const Quaternion = {
-    identity: (): QuatT => hostFn("createQuatIdentity")(),
-    fromEuler: (x: number, y: number, z: number): QuatT => hostFn("createQuatFromEuler")(x, y, z),
-};
-
-// make global
-export { Transform, Vec3, Quaternion };
-
-globalThis.Transform = Transform;
-globalThis.Vec3 = Vec3;
-globalThis.Quaternion = Quaternion;
\ No newline at end of file
diff --git a/redback-runtime b/redback-runtime
index 3169130..36de5a6 160000
--- a/redback-runtime
+++ b/redback-runtime
@@ -1 +1 @@
-Subproject commit 316913058a1f25b68b52e06c3d9069d3d3c73ffc
+Subproject commit 36de5a65d6cb8c77d30d3d3170ac5794d94d7e01