kitgit

tirbofish/dropbear · diff

f5ccc40 · Thribhu K

wip: implemented JNI function headers (finally). doing some testing.

Unverified

diff --git a/build.gradle.kts b/build.gradle.kts
index 9d7c8ea..06a66f0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -32,11 +32,17 @@ val libPathProvider = provider {
         layout.projectDirectory.file("libs/$libName").asFile
     )
 
-    candidates.firstOrNull { it.exists() }?.absolutePath ?: ""
+    val foundFile = candidates.firstOrNull { it.exists() }
+    if (foundFile != null) {
+        foundFile.absolutePath
+    } else {
+        println("No Rust library exists")
+        ""
+    }
 }
 
 kotlin {
-    jvm { }
+    jvm {}
 
     val nativeTarget = when {
         isMacOs && isArm64 -> macosArm64("nativeLib")
@@ -65,30 +71,33 @@ kotlin {
                 cinterops {
                     val dropbear by creating {
                         defFile(project.file("src/dropbear.def"))
-                        includeDirs.headerFilterOnly(project.file("headers"))
-                        compilerOpts("-I${project.file("headers").absolutePath}")
+                        includeDirs.headerFilterOnly(project.file("include"))
+                        compilerOpts("-I${project.file("include").absolutePath}")
                     }
                 }
             }
             binaries {
                 sharedLib {
                     baseName = "dropbear"
+
                     if (isLinux || isMacOs) {
                         linkerOpts("-L$nativeLibDir", "-l$nativeLibNameForLinking", "-Wl,-rpath,\\\$ORIGIN")
                     } else if (isMingwX64) {
-                        linkerOpts(file("$nativeLibDir/$nativeLibNameForLinking.dll.lib").absolutePath)
+                        val importLibName = "$nativeLibNameForLinking.dll.lib"
+                        val importLibPath = file("$nativeLibDir/$importLibName").absolutePath
+                        linkerOpts(importLibPath)
                     }
                 }
             }
         }
     } else {
-        println("WARNING: Rust library not found. Native compilation will skip linking against eucalyptus_core.")
+        println("Skipping native target configuration due to missing library path.")
         nativeTarget.apply {
             compilations.getByName("main") {
                 cinterops {
                     val dropbear by creating {
                         defFile(project.file("src/dropbear.def"))
-                        includeDirs.headerFilterOnly(project.file("headers"))
+                        includeDirs.headerFilterOnly(project.file("include"))
                     }
                 }
             }
@@ -98,7 +107,7 @@ kotlin {
     sourceSets {
         commonMain {
             dependencies {
-                api("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
+                api("org.jetbrains.kotlinx:kotlinx-datetime:0.7.0")
             }
         }
         nativeMain {
@@ -109,9 +118,8 @@ kotlin {
 
         jvmMain {
             kotlin.srcDirs("src/jvmMain/kotlin", "build/magna-carta")
-
             dependencies {
-                implementation(kotlin("stdlib"))
+
             }
         }
     }
@@ -127,116 +135,40 @@ kotlin {
     }
 }
 
-val extractJavaSources by tasks.registering(Copy::class) {
-    group = "jni"
-    description = "Copies .java files from jvmMain/kotlin to a temp directory for header generation"
-
-    from("src/jvmMain/kotlin")
-    include("**/*.java")
-    into(layout.buildDirectory.dir("tmp/java-jni-sources"))
-}
-
-val compileJavaForJni by tasks.registering(JavaCompile::class) {
-    group = "jni"
-    description = "Compiles Java sources and generates JNI headers"
-
-    dependsOn(extractJavaSources, "compileKotlinJvm")
+tasks.register<JavaCompile>("generateJniHeaders") {
+    val outputDir = layout.buildDirectory.dir("generated/jni-include")
+    options.headerOutputDirectory.set(outputDir.get().asFile)
 
-    source(extractJavaSources.map { it.destinationDir })
+    destinationDirectory.set(layout.buildDirectory.dir("classes/java/jni"))
 
     classpath = files(
-        tasks.named("compileKotlinJvm").map { it.outputs.files },
-        configurations.named("jvmCompileClasspath")
+        tasks.named("compileKotlinJvm"),
     )
 
-    destinationDirectory.set(layout.buildDirectory.dir("tmp/jni-java-classes"))
-
-    val headerOutputDir = layout.buildDirectory.dir("generated/jni-headers")
-    options.headerOutputDirectory.set(headerOutputDir)
-
-    doFirst {
-        val outDir = headerOutputDir.get().asFile
-        if (outDir.exists()) {
-            outDir.deleteRecursively()
-        }
-        outDir.mkdirs()
+    source = fileTree("src/jvmMain/kotlin") {
+        include("**/*.java")
     }
-}
-
-val generateKotlinJniHeaders by tasks.registering(Exec::class) {
-    group = "jni"
-    description = "Generates JNI headers from Kotlin external functions"
 
     dependsOn("compileKotlinJvm")
 
-    val headerOutputDir = layout.buildDirectory.dir("generated/jni-headers")
-    val kotlinClassesDir = tasks.named("compileKotlinJvm").map {
-        it.outputs.files.filter { f -> f.isDirectory }
-    }
-
-    inputs.files(kotlinClassesDir)
-    outputs.dir(headerOutputDir)
-
     doFirst {
-        val outDir = headerOutputDir.get().asFile
-        if (!outDir.exists()) {
-            outDir.mkdirs()
+        val javaFiles = source.files
+        if (javaFiles.isEmpty()) {
+            println("WARNING: No Java files found in src/jvmMain/kotlin for JNI header generation")
+        } else {
+            println("Generating JNI include for ${javaFiles.size} Java files:")
+            javaFiles.forEach { println("  - ${it.name}") }
         }
-
-        val classFiles = kotlinClassesDir.get().asFileTree
-            .filter { it.name.endsWith(".class") && !it.name.contains("$") }
-            .files
-
-        if (classFiles.isEmpty()) {
-            println("No Kotlin class files found for JNI header generation")
-            return@doFirst
-        }
-
-        val classpath = kotlinClassesDir.get().joinToString(File.pathSeparator) { it.absolutePath }
-
-        classFiles.forEach { classFile ->
-            val baseDir = kotlinClassesDir.get().first { classFile.startsWith(it) }
-            val relativePath = classFile.relativeTo(baseDir).path
-            val className = relativePath.removeSuffix(".class").replace(File.separatorChar, '.')
-
-            // Use javah (Java 8) or javac -h (Java 9+)
-            try {
-                exec {
-                    commandLine(
-                        "javac", "-h", outDir.absolutePath,
-                        "-cp", classpath,
-                        "-d", layout.buildDirectory.dir("tmp/jni-dummy-classes").get().asFile.absolutePath,
-                        classFile.absolutePath
-                    )
-                    isIgnoreExitValue = true
-                }
-            } catch (e: Exception) {
-                println("Warning: Could not generate header for $className: ${e.message}")
-            }
-        }
-
-        println("Generated Kotlin JNI Headers at: ${outDir.absolutePath}")
     }
-}
-
-val generateJniHeaders by tasks.registering {
-    group = "jni"
-    description = "Generates all JNI headers (Java and Kotlin)"
-
-    dependsOn(compileJavaForJni, generateKotlinJniHeaders)
 
     doLast {
-        val headerDir = layout.buildDirectory.dir("generated/jni-headers").get().asFile
+        val headerDir = outputDir.get().asFile
         val headers = headerDir.listFiles()?.filter { it.extension == "h" } ?: emptyList()
-        println("Total JNI headers generated: ${headers.size}")
+        println("Generated ${headers.size} JNI include:")
         headers.forEach { println("  - ${it.name}") }
     }
 }
 
-tasks.named("jvmMainClasses") {
-    dependsOn(generateJniHeaders)
-}
-
 publishing {
     repositories {
         maven {
@@ -248,17 +180,13 @@ publishing {
     publications.withType<MavenPublication> {
         pom {
             name.set("dropbear")
-            description.set("The dropbear scripting part of the engine")
+            description.set("The dropbear scripting part of the engine... uhh yeah!")
             url.set("https://github.com/tirbofish/dropbear")
 
             licenses {
                 license {
-                    name.set("MIT")
-                    url.set("https://opensource.org/license/mit")
-                }
-                license {
-                    name.set("Apache-2.0")
-                    url.set("https://www.apache.org/licenses/LICENSE-2.0")
+                    name.set("dropbear engine License, Version 1.2")
+                    url.set("https://raw.githubusercontent.com/tirbofish/dropbear/refs/heads/main/LICENSE.md")
                 }
             }
 
@@ -285,11 +213,13 @@ tasks.register<Jar>("fatJar") {
 
     from(kotlin.jvm().compilations["main"].output)
 
-    from(configurations.named("jvmRuntimeClasspath").map {
-        it.map { file -> if (file.isDirectory) file else zipTree(file) }
-    })
-
-    manifest {
-        attributes["Implementation-Version"] = project.version
+    configurations.named("jvmRuntimeClasspath").get().forEach { file ->
+        if (file.name.endsWith(".jar")) {
+            from(zipTree(file))
+        } else {
+            from(file)
+        }
     }
+
+    manifest {}
 }
\ No newline at end of file
diff --git a/dropbear-gradle-plugin/build.gradle.kts b/dropbear-gradle-plugin/build.gradle.kts
deleted file mode 100644
index 7a511fe..0000000
--- a/dropbear-gradle-plugin/build.gradle.kts
+++ /dev/null
@@ -1,72 +0,0 @@
-import org.gradle.kotlin.dsl.compileOnly
-
-plugins {
-    `kotlin-dsl`
-    `maven-publish`
-    id("com.gradle.plugin-publish") version "2.0.0"
-}
-
-group = "com.dropbear"
-version = "1.0-SNAPSHOT"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    implementation("de.undercouch:gradle-download-task:5.6.0")
-    compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:${KotlinVersion.CURRENT}")
-}
-
-gradlePlugin {
-    website.set("https://github.com/tirbofish/dropbear")
-    vcsUrl.set("https://github.com/tirbofish/dropbear")
-    plugins {
-
-        create("dropbearGradlePlugin") {
-            id = "dropbear-gradle-plugin"
-            implementationClass = "com.dropbear.gradle.DropbearGradlePlugin"
-            displayName = "dropbear-gradle-plugin"
-            description = "Gradle plugin for dependency management for dropbear-based projects"
-            version = version as String
-        }
-    }
-}
-
-publishing {
-    publications {
-        withType<MavenPublication>().configureEach {
-            pom {
-                name.set("dropbear-gradle-plugin")
-                description.set("Gradle plugin for dropbear dependency management")
-                url.set("https://tirbofish.github.io/dropbear/")
-
-                licenses {
-                    license {
-                        name.set("MIT License")
-                        url.set("https://opensource.org/licenses/MIT")
-                    }
-                }
-                developers {
-                    developer {
-                        id.set("tirbofish")
-                        name.set("tk")
-                        email.set("tirbofish@pm.me")
-                    }
-                }
-                scm {
-                    connection.set("scm:git:git://github.com/tirbofish/dropbear.git")
-                    developerConnection.set("scm:git:ssh://github.com/tirbofish/dropbear.git")
-                    url.set("https://github.com/tirbofish/dropbear")
-                }
-            }
-        }
-    }
-
-    repositories {
-        maven {
-            name = "GitHubPages"
-            url = uri("${layout.buildDirectory}/repo")
-        }
-    }
-}
\ No newline at end of file
diff --git a/dropbear-gradle-plugin/src/main/kotlin/com/dropbear/gradle/DropbearGradlePlugin.kt b/dropbear-gradle-plugin/src/main/kotlin/com/dropbear/gradle/DropbearGradlePlugin.kt
deleted file mode 100644
index 5d0c24c..0000000
--- a/dropbear-gradle-plugin/src/main/kotlin/com/dropbear/gradle/DropbearGradlePlugin.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.dropbear.gradle
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-
-class DropbearGradlePlugin: Plugin<Project> {
-    override fun apply(target: Project) {
-        TODO("Not yet implemented")
-    }
-}
\ No newline at end of file
diff --git a/eucalyptus-core/README.md b/eucalyptus-core/README.md
index 93da694..b73f4cd 100644
--- a/eucalyptus-core/README.md
+++ b/eucalyptus-core/README.md
@@ -2,13 +2,39 @@
 
 The core libraries of the eucalyptus editor. Great for embedding into `redback-runtime` and `eucalyptus-editor` as one big change instead of a bunch of features.
 
-This is a library, so if tools are wished to be made, this is the perfect library for you.
+It also produces a shared library for Kotlin/Native and the JVM.
 
-it also produces a shared library for Kotlin/Native and the JVM :)
+Kotlin Multiplatform is prioritised and fully supported on this library, however that is not to say that you (yes you 
+the reader) could implement this for another language (as really it just reads from a .dll or a .jar file). 
 
-## Features
+[//]: # (## Export requirements)
 
-- `editor` - Enables editor only features that the redback-runtime would not be able to access
-- `jvm` - Enables the JVM as a ScriptTarget and running the Java Virtual Machine (not possible with non-desktop targets)
-- `jvm_debug` - Enables debugging of the JVM through the java debugger. Can pose a risk to tampering, so is disabled by default unless
-    want to be enabled by developer or enabled by default by the `editor` feature. 
\ No newline at end of file
+[//]: # ()
+[//]: # (To have your own scripting module to be able to be run, you will have to implement the following exports into)
+
+[//]: # (your own app:)
+
+[//]: # ()
+[//]: # (- `dropbear_init`)
+
+[//]: # (  - Args: `pointerContext: DropbearContext*`)
+
+[//]: # (  - Returns: `int`)
+
+[//]: # (- `dropbear_load_tagged`)
+
+[//]: # (  - Args: `tag: *mut c_char`)
+
+[//]: # (  - Returns: `int`)
+
+[//]: # (- `dropbear_update_all`)
+
+[//]: # (  - Args: `dt: float`)
+
+[//]: # (  - Returns: `int`)
+
+[//]: # (- `dropbear_update_tagged`)
+
+[//]: # (  - Args: `tag: *mut c_char, `)
+
+[//]: # (  - )
\ No newline at end of file
diff --git a/eucalyptus-core/build.rs b/eucalyptus-core/build.rs
index eaba71d..9d23613 100644
--- a/eucalyptus-core/build.rs
+++ b/eucalyptus-core/build.rs
@@ -1,36 +1,4 @@
-use std::env;
-use std::path::PathBuf;
-
 fn main() -> anyhow::Result<()> {
-    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
-
-    let root_dir = PathBuf::from(&crate_dir).parent().unwrap().to_path_buf();
-
-    let expanded = std::process::Command::new("cargo")
-        .args(&["expand", "--lib", "-p", "eucalyptus-core"])
-        .current_dir(&root_dir)
-        .output()?;
-
-    let temp_file = "target/generated/expanded.rs";
-    let temp_file = root_dir.join(temp_file);
-    std::fs::create_dir_all(temp_file.parent().unwrap())?;
-    std::fs::write(&root_dir.join(&temp_file), expanded.stderr)?;
-
-    let output_file = root_dir
-        .join("headers")
-        .join("dropbear.h");
-
-    let config = cbindgen::Config::from_file("../cbindgen.toml")
-        .expect("Could not find cbindgen.toml");
-
-    cbindgen::Builder::new()
-        .with_src(temp_file)
-        .with_config(config)
-        .generate()
-        .expect("Unable to generate bindings")
-        .write_to_file(output_file);
-
-    println!("cargo:rerun-if-changed=cbindgen.toml");
 
     // fuck you windows :(
     #[cfg(target_os = "windows")]
diff --git a/eucalyptus-core/src/asset.rs b/eucalyptus-core/src/asset.rs
index fa00e93..399baf5 100644
--- a/eucalyptus-core/src/asset.rs
+++ b/eucalyptus-core/src/asset.rs
@@ -33,7 +33,7 @@ pub mod jni {
     use crate::convert_ptr;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_asset_AssetHandleNative_isModelHandle(
+    pub extern "system" fn Java_com_com_dropbear_asset_AssetHandleNative_isModelHandle(
         _env: JNIEnv,
         _class: JClass,
         asset_registry_ptr: jlong,
@@ -50,7 +50,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_asset_AssetHandleNative_isTextureHandle(
+    pub extern "system" fn Java_com_com_dropbear_asset_AssetHandleNative_isTextureHandle(
         _env: JNIEnv,
         _class: JClass,
         asset_registry_ptr: jlong,
diff --git a/eucalyptus-core/src/asset/texture.rs b/eucalyptus-core/src/asset/texture.rs
index 0e30f30..7e37fcc 100644
--- a/eucalyptus-core/src/asset/texture.rs
+++ b/eucalyptus-core/src/asset/texture.rs
@@ -25,7 +25,7 @@ pub mod jni {
     use crate::scripting::native::DropbearNativeError;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_asset_TextureHandleNative_getTextureName(
+    pub extern "system" fn Java_com_dropbear_asset_TextureHandleNative_getTextureName(
         env: JNIEnv,
         _class: JClass,
         asset_registry_ptr: jlong,
diff --git a/eucalyptus-core/src/camera.rs b/eucalyptus-core/src/camera.rs
index af4e967..7ccd606 100644
--- a/eucalyptus-core/src/camera.rs
+++ b/eucalyptus-core/src/camera.rs
@@ -137,7 +137,7 @@ pub mod jni {
     use crate::scripting::jni::utils::{FromJObject, ToJObject};
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_cameraExistsForEntity(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_cameraExistsForEntity(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -153,7 +153,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraEye(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraEye(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -174,7 +174,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraEye(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraEye(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -199,7 +199,7 @@ pub mod jni {
 
     // TARGET
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraTarget(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraTarget(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -220,7 +220,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraTarget(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraTarget(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -239,7 +239,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraUp(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraUp(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -260,7 +260,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraUp(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraUp(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -279,7 +279,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraAspect(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraAspect(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -295,7 +295,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraFovY(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraFovY(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -311,7 +311,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraFovY(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraFovY(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -326,7 +326,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraZNear(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraZNear(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -342,7 +342,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraZNear(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraZNear(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -357,7 +357,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraZFar(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraZFar(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -373,7 +373,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraZFar(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraZFar(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -388,7 +388,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraYaw(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraYaw(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -404,7 +404,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraYaw(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraYaw(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -419,7 +419,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraPitch(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraPitch(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -435,7 +435,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraPitch(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraPitch(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -450,7 +450,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraSpeed(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraSpeed(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -466,7 +466,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraSpeed(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraSpeed(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -481,7 +481,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_getCameraSensitivity(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_getCameraSensitivity(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -497,7 +497,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CameraNative_setCameraSensitivity(
+    pub extern "system" fn Java_com_com_dropbear_components_CameraNative_setCameraSensitivity(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
diff --git a/eucalyptus-core/src/engine.rs b/eucalyptus-core/src/engine.rs
index d946533..65714db 100644
--- a/eucalyptus-core/src/engine.rs
+++ b/eucalyptus-core/src/engine.rs
@@ -66,7 +66,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_DropbearEngineNative_getEntity(
+    pub extern "system" fn Java_com_com_dropbear_DropbearEngineNative_getEntity(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -88,7 +88,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_DropbearEngineNative_getAsset(
+    pub extern "system" fn Java_com_com_dropbear_DropbearEngineNative_getAsset(
         mut env: JNIEnv,
         _class: JClass,
         asset_handle: jlong,
@@ -110,7 +110,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_DropbearEngineNative_quit(
+    pub extern "system" fn Java_com_com_dropbear_DropbearEngineNative_quit(
         mut env: JNIEnv,
         _class: JClass,
         command_buffer_ptr: jlong,
diff --git a/eucalyptus-core/src/entity.rs b/eucalyptus-core/src/entity.rs
index bb9a87e..730f655 100644
--- a/eucalyptus-core/src/entity.rs
+++ b/eucalyptus-core/src/entity.rs
@@ -9,7 +9,7 @@ pub mod jni {
     use jni::JNIEnv;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_EntityRefNative_getEntityLabel(
+    pub extern "system" fn Java_com_com_dropbear_EntityRefNative_getEntityLabel(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -33,7 +33,7 @@ pub mod jni {
         string.into_raw()
     }
 
-    pub fn Java_com_dropbear_EntityRefNative_getChildren(
+    pub extern "system" fn Java_com_com_dropbear_EntityRefNative_getChildren(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -64,7 +64,7 @@ pub mod jni {
         }
     }
 
-    pub fn Java_com_dropbear_EntityRefNative_getChildByLabel(
+    pub extern "system" fn Java_com_com_dropbear_EntityRefNative_getChildByLabel(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -98,7 +98,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_EntityRefNative_getParent(
+    pub extern "system" fn Java_com_com_dropbear_EntityRefNative_getParent(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
diff --git a/eucalyptus-core/src/input.rs b/eucalyptus-core/src/input.rs
index a34adac..4efca24 100644
--- a/eucalyptus-core/src/input.rs
+++ b/eucalyptus-core/src/input.rs
@@ -200,7 +200,7 @@ pub mod jni {
     use crate::scripting::jni::utils::ToJObject;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_printInputState(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_printInputState(
         _env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -210,7 +210,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_isKeyPressed(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_isKeyPressed(
         _env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -221,7 +221,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_getMousePosition(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_getMousePosition(
         mut env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -235,7 +235,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_isMouseButtonPressed(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_isMouseButtonPressed(
         mut env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -257,7 +257,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_getMouseDelta(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_getMouseDelta(
         mut env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -271,7 +271,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_isCursorLocked(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_isCursorLocked(
         _env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -281,7 +281,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_setCursorLocked(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_setCursorLocked(
         mut env: JNIEnv,
         _class: JClass,
         cmd_ptr: jlong,
@@ -297,7 +297,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_getLastMousePos(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_getLastMousePos(
         mut env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -311,7 +311,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_isCursorHidden(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_isCursorHidden(
         _env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -321,7 +321,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_setCursorHidden(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_setCursorHidden(
         mut env: JNIEnv,
         _class: JClass,
         cmd_ptr: jlong,
@@ -337,7 +337,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_InputStateNative_getConnectedGamepads(
+    pub extern "system" fn Java_com_com_dropbear_input_InputStateNative_getConnectedGamepads(
         env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
diff --git a/eucalyptus-core/src/input/gamepad.rs b/eucalyptus-core/src/input/gamepad.rs
index 0455131..2b99487 100644
--- a/eucalyptus-core/src/input/gamepad.rs
+++ b/eucalyptus-core/src/input/gamepad.rs
@@ -132,7 +132,7 @@ pub mod jni {
     use crate::scripting::jni::utils::ToJObject;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_GamepadNative_isGamepadButtonPressed(
+    pub extern "system" fn Java_com_dropbear_input_GamepadNative_isGamepadButtonPressed(
         _env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -148,7 +148,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_GamepadNative_getLeftStickPosition(
+    pub extern "system" fn Java_com_dropbear_input_GamepadNative_getLeftStickPosition(
         mut env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
@@ -164,7 +164,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_input_GamepadNative_getRightStickPosition(
+    pub extern "system" fn Java_com_dropbear_input_GamepadNative_getRightStickPosition(
         mut env: JNIEnv,
         _class: JClass,
         input_ptr: jlong,
diff --git a/eucalyptus-core/src/mesh.rs b/eucalyptus-core/src/mesh.rs
index 5eda4a0..6bf4e26 100644
--- a/eucalyptus-core/src/mesh.rs
+++ b/eucalyptus-core/src/mesh.rs
@@ -41,7 +41,7 @@ pub mod jni {
     use std::sync::Arc;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_MeshRendererNative_meshRendererExistsForEntity(
+    pub extern "system" fn Java_com_com_dropbear_components_MeshRendererNative_meshRendererExistsForEntity(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -53,7 +53,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_MeshRendererNative_getModel(
+    pub extern "system" fn Java_com_com_dropbear_components_MeshRendererNative_getModel(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -71,7 +71,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_MeshRendererNative_setModel(
+    pub extern "system" fn Java_com_com_dropbear_components_MeshRendererNative_setModel(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -96,7 +96,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_MeshRendererNative_getAllTextureIds(
+    pub extern "system" fn Java_com_com_dropbear_components_MeshRendererNative_getAllTextureIds(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -130,7 +130,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_MeshRendererNative_getTexture(
+    pub extern "system" fn Java_com_com_dropbear_components_MeshRendererNative_getTexture(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -154,7 +154,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_MeshRendererNative_setTextureOverride(
+    pub extern "system" fn Java_com_com_dropbear_components_MeshRendererNative_setTextureOverride(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
diff --git a/eucalyptus-core/src/physics.rs b/eucalyptus-core/src/physics.rs
index 40de982..6a3d5a0 100644
--- a/eucalyptus-core/src/physics.rs
+++ b/eucalyptus-core/src/physics.rs
@@ -18,19 +18,32 @@ pub mod collider;
 /// to physics rendering.
 #[derive(Serialize, Deserialize, Clone)]
 pub struct PhysicsState {
+    #[serde(default)]
     pub islands: IslandManager,
+    #[serde(default)]
     pub broad_phase: DefaultBroadPhase,
+    #[serde(default)]
     pub narrow_phase: NarrowPhase,
+    #[serde(default)]
     pub bodies: RigidBodySet,
+    #[serde(default)]
     pub colliders: ColliderSet,
+    #[serde(default)]
     pub impulse_joints: ImpulseJointSet,
+    #[serde(default)]
     pub multibody_joints: MultibodyJointSet,
+    #[serde(default)]
     pub ccd_solver: CCDSolver,
+    #[serde(default)]
     pub integration_parameters: IntegrationParameters,
+
     pub gravity: [f32; 3],
 
+    #[serde(default)]
     pub bodies_entity_map: HashMap<Label, RigidBodyHandle>,
+    #[serde(default)]
     pub colliders_entity_map: HashMap<Label, Vec<(u32, ColliderHandle)>>,
+    #[serde(default)]
     pub entity_label_map: HashMap<Entity, Label>,
 }
 
@@ -89,6 +102,7 @@ impl PhysicsState {
                 rot[3] as f32, rot[0] as f32, rot[1] as f32, rot[2] as f32
             )).scaled_axis())
             .gravity_scale(rigid_body.gravity_scale)
+            .sleeping(rigid_body.sleeping)
             .can_sleep(rigid_body.can_sleep)
             .ccd_enabled(rigid_body.ccd_enabled)
             .linvel(Vector3::from_column_slice(&rigid_body.linvel))
diff --git a/eucalyptus-core/src/physics/collider.rs b/eucalyptus-core/src/physics/collider.rs
index 567e6b5..aa8a7bf 100644
--- a/eucalyptus-core/src/physics/collider.rs
+++ b/eucalyptus-core/src/physics/collider.rs
@@ -615,7 +615,7 @@ pub mod jni {
     use crate::physics::collider::ColliderShape;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderShape(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderShape(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -680,7 +680,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderShape(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderShape(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -728,7 +728,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderDensity(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderDensity(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -745,7 +745,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderDensity(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderDensity(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -761,7 +761,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderFriction(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderFriction(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -775,7 +775,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderFriction(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderFriction(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -791,7 +791,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderRestitution(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderRestitution(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -805,7 +805,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderRestitution(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderRestitution(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -821,7 +821,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderMass(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderMass(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -835,7 +835,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderMass(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderMass(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -851,7 +851,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderIsSensor(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderIsSensor(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -865,7 +865,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderIsSensor(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderIsSensor(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -881,7 +881,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderTranslation(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderTranslation(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -906,7 +906,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderTranslation(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderTranslation(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -925,7 +925,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_getColliderRotation(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderRotation(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
@@ -953,7 +953,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderNative_setColliderRotation(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderRotation(
         mut env: JNIEnv,
         _class: JClass,
         physics_ptr: jlong,
diff --git a/eucalyptus-core/src/physics/collider/collidergroup.rs b/eucalyptus-core/src/physics/collider/collidergroup.rs
index 9ca2f16..4395ccf 100644
--- a/eucalyptus-core/src/physics/collider/collidergroup.rs
+++ b/eucalyptus-core/src/physics/collider/collidergroup.rs
@@ -72,7 +72,7 @@ pub mod jni {
     use jni::JNIEnv;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderGroupNative_colliderGroupExistsForEntity(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderGroupNative_colliderGroupExistsForEntity(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -89,7 +89,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_physics_ColliderGroupNative_getColliderGroupColliders(
+    pub extern "system" fn Java_com_dropbear_physics_ColliderGroupNative_getColliderGroupColliders(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
diff --git a/eucalyptus-core/src/physics/rigidbody.rs b/eucalyptus-core/src/physics/rigidbody.rs
index 93a6c2b..19cfe55 100644
--- a/eucalyptus-core/src/physics/rigidbody.rs
+++ b/eucalyptus-core/src/physics/rigidbody.rs
@@ -1,17 +1,24 @@
 //! [rapier3d] RigidBodies
 
-use serde::{Deserialize, Serialize};
+use crate::scripting::jni::utils::{FromJObject, ToJObject};
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+use crate::states::Label;
 use dropbear_macro::SerializableComponent;
 use dropbear_traits::SerializableComponent;
-use crate::states::Label;
+use ::jni::objects::{JObject, JValue};
+use ::jni::JNIEnv;
+use rapier3d::prelude::RigidBodyType;
+use serde::{Deserialize, Serialize};
 
 /// How this entity behaves in the physics simulation.
 ///
 /// This intentionally mirrors Rapier's rigid-body types, but stays engine-owned and serializable.
+#[repr(C)]
 #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
 pub enum RigidBodyMode {
 	/// A fully simulated body affected by forces and contacts.
-	Dynamic,
+	Dynamic = 0,
 	/// An immovable body.
 	Fixed,
 	/// A kinematic body controlled by setting its next position.
@@ -26,6 +33,18 @@ impl Default for RigidBodyMode {
 	}
 }
 
+impl From<RigidBodyType> for RigidBodyMode {
+	fn from(value: RigidBodyType) -> Self {
+		match value {
+			RigidBodyType::Dynamic => RigidBodyMode::Dynamic,
+			RigidBodyType::Fixed => Self::Fixed,
+			RigidBodyType::KinematicPositionBased => Self::KinematicPosition,
+			RigidBodyType::KinematicVelocityBased => Self::KinematicVelocity,
+		}
+	}
+}
+
+#[repr(C)]
 #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
 pub struct AxisLock {
 	pub x: bool,
@@ -33,6 +52,59 @@ pub struct AxisLock {
 	pub z: bool,
 }
 
+impl FromJObject for AxisLock {
+	fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
+	where
+		Self: Sized
+	{
+		let class = env.find_class("com/dropbear/physics/AxisLock")
+			.map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+		if !env.is_instance_of(obj, &class)
+			.map_err(|_| DropbearNativeError::JNIUnwrapFailed)?
+		{
+			return Err(DropbearNativeError::InvalidArgument);
+		}
+
+		let x = env.get_field(obj, "x", "Z")
+			.map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+			.z()
+			.map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+		let y = env.get_field(obj, "y", "Z")
+			.map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+			.z()
+			.map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+		let z = env.get_field(obj, "z", "Z")
+			.map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+			.z()
+			.map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+		Ok(AxisLock { x, y, z })
+	}
+}
+
+impl ToJObject for AxisLock {
+	fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+		let class = env.find_class("com/dropbear/physics/AxisLock")
+			.map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+		let constructor_sig = "(ZZZ)V";
+
+		let args = [
+			JValue::Bool(self.x as u8),
+			JValue::Bool(self.y as u8),
+			JValue::Bool(self.z as u8),
+		];
+
+		let obj = env.new_object(&class, constructor_sig, &args)
+			.map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+		Ok(obj)
+	}
+}
+
 /// A serializable physics rigid-body component.
 ///
 /// Notes:
@@ -57,9 +129,12 @@ pub struct RigidBody {
 	/// Scaling factor applied to gravity for this body.
 	#[serde(default = "RigidBody::default_gravity_scale")]
 	pub gravity_scale: f32,
+	
+	/// If the rigidbody is currently sleeping or not. 
+	pub sleeping: bool,
 
 	/// Whether this body is allowed to sleep.
-	#[serde(default = "RigidBody::default_can_sleep")]
+	#[serde(default = "RigidBody::default_sleep")]
 	pub can_sleep: bool,
 
 	/// Whether continuous collision detection is enabled.
@@ -98,7 +173,8 @@ impl Default for RigidBody {
 			disable_physics: false,
 			mode: RigidBodyMode::default(),
 			gravity_scale: Self::default_gravity_scale(),
-			can_sleep: Self::default_can_sleep(),
+			sleeping: false,
+			can_sleep: Self::default_sleep(),
 			ccd_enabled: false,
 			linvel: [0.0, 0.0, 0.0],
 			angvel: [0.0, 0.0, 0.0],
@@ -115,17 +191,1093 @@ impl RigidBody {
 		1.0
 	}
 
-	const fn default_can_sleep() -> bool {
+	const fn default_sleep() -> bool {
 		true
 	}
 }
 
 pub mod shared {
+	use crate::physics::rigidbody::{AxisLock, RigidBody, RigidBodyMode};
+	use crate::physics::PhysicsState;
+	use crate::scripting::native::DropbearNativeError;
+	use crate::scripting::result::DropbearNativeResult;
+	use crate::states::Label;
+	use crate::types::{IndexNative, RigidBodyContext, Vector3};
+	use hecs::{Entity, World};
+	use rapier3d::dynamics::{RigidBodyHandle, RigidBodyType};
+	use rapier3d::prelude::vector;
+	use rapier3d::prelude::{nalgebra, ColliderHandle, LockedAxes};
+
+	pub fn rigid_body_exists_for_entity(
+		world: &World,
+		physics: &PhysicsState,
+		entity: Entity
+	) -> Option<IndexNative> {
+		if let Ok(mut q) = world.query_one::<(&Label, &RigidBody)>(entity)
+			&& let Some((label, _)) = q.get()
+		{
+			if let Some(handle) = physics.bodies_entity_map.get(label) {
+				Some(IndexNative::from(handle.0))
+			} else {
+				None
+			}
+		} else {
+			None
+		}
+	}
+
+	pub fn get_rigidbody_type(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<RigidBodyType> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			Ok(rb.body_type())
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_type(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, mode: i64) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			let mode = match mode {
+				0 => RigidBodyType::Dynamic,
+				1 => RigidBodyType::Fixed,
+				2 => RigidBodyType::KinematicPositionBased,
+				3 => RigidBodyType::KinematicVelocityBased,
+				_ => { return Err(DropbearNativeError::InvalidArgument); }
+			};
+			rb.set_body_type(mode, true);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				let rb_mode = RigidBodyMode::from(mode);
+				rb.mode = rb_mode;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_gravity_scale(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<f64> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			Ok(rb.gravity_scale().into())
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
 	
+	pub fn set_rigidbody_gravity_scale(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new_scale: f64) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.set_gravity_scale(new_scale as f32, true);
+			
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.gravity_scale = new_scale as f32;				
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_linear_damping(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<f64> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			Ok(rb.linear_damping().into())
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_linear_damping(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: f64) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.set_linear_damping(new as f32);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.linear_damping = new as f32;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_angular_damping(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<f64> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			Ok(rb.angular_damping().into())
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_angular_damping(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: f64) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.set_angular_damping(new as f32);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.angular_damping = new as f32;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_sleep(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<bool> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			Ok(rb.is_sleeping().into())
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_sleep(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: bool) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			if new {
+				rb.sleep();
+			} else {
+				rb.wake_up(true);
+			}
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.sleeping = new;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_ccd(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<bool> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			Ok(rb.is_ccd_enabled().into())
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_ccd(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: bool) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.enable_ccd(new);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.ccd_enabled = new;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_linvel(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<Vector3> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			let linvel = rb.linvel().clone();
+			Ok(Vector3::new(linvel.x as f64, linvel.y as f64, linvel.z as f64))
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_linvel(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: Vector3) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.set_linvel(vector![new.x as f32, new.y as f32, new.z as f32], true);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.linvel = [new.x as f32, new.y as f32, new.z as f32];
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_angvel(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<Vector3> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			let angvel = rb.angvel().clone();
+			Ok(Vector3::new(angvel.x as f64, angvel.y as f64, angvel.z as f64))
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_angvel(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: Vector3) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.set_angvel(vector![new.x as f32, new.y as f32, new.z as f32], true);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.angvel = [new.x as f32, new.y as f32, new.z as f32];
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_lock_translation(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<AxisLock> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			let lock = rb.locked_axes();
+			let translation_lock = AxisLock {
+				x: lock.contains(LockedAxes::TRANSLATION_LOCKED_X),
+				y: lock.contains(LockedAxes::TRANSLATION_LOCKED_Y),
+				z: lock.contains(LockedAxes::TRANSLATION_LOCKED_Z),
+			};
+			Ok(translation_lock)
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_lock_translation(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			let mut bits = rb.locked_axes().clone();
+
+			bits.remove(LockedAxes::TRANSLATION_LOCKED_X);
+			bits.remove(LockedAxes::TRANSLATION_LOCKED_Y);
+			bits.remove(LockedAxes::TRANSLATION_LOCKED_Z);
+			
+			if new.x {
+				bits = bits | LockedAxes::TRANSLATION_LOCKED_X;
+			}
+			if new.y {
+				bits = bits | LockedAxes::TRANSLATION_LOCKED_Y;
+			}
+			if new.z {
+				bits = bits | LockedAxes::TRANSLATION_LOCKED_Z;
+			}
+			
+			rb.set_locked_axes(bits, true);
+			
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.lock_translation = new;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_lock_rotation(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<AxisLock> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			let lock = rb.locked_axes();
+			let rot_lock = AxisLock {
+				x: lock.contains(LockedAxes::ROTATION_LOCKED_X),
+				y: lock.contains(LockedAxes::ROTATION_LOCKED_Y),
+				z: lock.contains(LockedAxes::ROTATION_LOCKED_Z),
+			};
+			Ok(rot_lock)
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn set_rigidbody_lock_rotation(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			let mut bits = rb.locked_axes().clone();
+			
+			bits.remove(LockedAxes::ROTATION_LOCKED_X);
+			bits.remove(LockedAxes::ROTATION_LOCKED_Y);
+			bits.remove(LockedAxes::ROTATION_LOCKED_Z);
+
+			if new.x {
+				bits = bits | LockedAxes::ROTATION_LOCKED_X;
+			}
+			if new.y {
+				bits = bits | LockedAxes::ROTATION_LOCKED_Y;
+			}
+			if new.z {
+				bits = bits | LockedAxes::ROTATION_LOCKED_Z;
+			}
+
+			rb.set_locked_axes(bits, true);
+
+			let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+
+			if let Ok(mut q) = world.query_one::<&mut RigidBody>(entity) && let Some(rb) = q.get() {
+				rb.lock_translation = new;
+			}
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn get_rigidbody_children(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<Vec<ColliderHandle>> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get(handle) {
+			let children = rb.colliders().to_vec();
+			Ok(children)
+		} else {
+			Err(DropbearNativeError::PhysicsObjectNotFound)
+		}
+	}
+
+	pub fn apply_impulse(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: Vector3) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.apply_impulse(vector![new.x as f32, new.y as f32, new.z as f32], true);
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
+
+	pub fn apply_torque_impulse(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: Vector3) -> DropbearNativeResult<()> {
+		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
+		if let Some(rb) = physics.bodies.get_mut(handle) {
+			rb.apply_torque_impulse(vector![new.x as f32, new.y as f32, new.z as f32], true);
+
+			Ok(())
+		} else {
+			Err(DropbearNativeError::NoSuchHandle)
+		}
+	}
 }
 
 pub mod jni {
-	
+	#![allow(non_snake_case)]
+	use crate::physics::rigidbody::AxisLock;
+	use crate::physics::PhysicsState;
+	use crate::scripting::jni::utils::{FromJObject, ToJObject};
+	use crate::types::{IndexNative, RigidBodyContext, Vector3};
+	use crate::{convert_jlong_to_entity, convert_ptr};
+	use hecs::World;
+	use jni::objects::{JClass, JObject};
+	use jni::sys::{jboolean, jdouble, jint, jlong, jobject, jobjectArray, jsize};
+	use jni::JNIEnv;
+	use rapier3d::dynamics::RigidBodyType;
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_rigidBodyExistsForEntity(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		entity_id: jlong,
+	) -> jobject {
+		let world = convert_ptr!(world_ptr => World);
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let entity = convert_jlong_to_entity!(entity_id);
+
+		match super::shared::rigid_body_exists_for_entity(world, physics, entity) {
+			Some(v) => {
+				match v.to_jobject(&mut env) {
+					Ok(val) => val.into_raw(),
+					Err(e) => {
+						eprintln!("Failed to create new Index jobject: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create new Index jobject: {}", e));
+						std::ptr::null_mut()
+					}
+				}
+			}
+			None => std::ptr::null_mut()
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyMode(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jint {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException", "Unable to convert com/dropbear/physics/RigidBody into a Rust rigidbody");
+			return -1 as jint;
+		};
+
+		match super::shared::get_rigidbody_type(physics, rb_context) {
+			Ok(v) => {
+				match v {
+					RigidBodyType::Dynamic => 0 as jint,
+					RigidBodyType::Fixed => 1 as jint,
+					RigidBodyType::KinematicPositionBased => 2 as jint,
+					RigidBodyType::KinematicVelocityBased => 3 as jint,
+				}
+			}
+			Err(e) => {
+				eprintln!("Failed to get RigidBody type: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody type: {}", e));
+				-1 as jint
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyMode(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		mode: jint,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_type(&mut physics, world, rb_context, mode as i64) {
+			eprintln!("Failed to set RigidBody Type: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody type: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyGravityScale(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jdouble {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return -1 as jdouble;
+		};
+
+		match super::shared::get_rigidbody_gravity_scale(physics, rb_context) {
+			Ok(v) => v as jdouble,
+			Err(e) => {
+				eprintln!("Failed to get RigidBody component: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody component: {}", e));
+				-1 as jdouble
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyGravityScale(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		gravity_scale: jdouble,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_gravity_scale(&mut physics, world, rb_context, gravity_scale as f64) {
+			eprintln!("Failed to set RigidBody gravity scale: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody gravity scale: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLinearDamping(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jdouble {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return -1 as jdouble;
+		};
+
+		match super::shared::get_rigidbody_linear_damping(physics, rb_context) {
+			Ok(v) => v as jdouble,
+			Err(e) => {
+				eprintln!("Failed to get RigidBody linear damping: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody linear damping: {}", e));
+				-1 as jdouble
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLinearDamping(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		linear_damping: jdouble,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_linear_damping(&mut physics, world, rb_context, linear_damping as f64) {
+			eprintln!("Failed to set RigidBody linear damping: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody linear damping: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyAngularDamping(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jdouble {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return -1 as jdouble;
+		};
+
+		match super::shared::get_rigidbody_angular_damping(physics, rb_context) {
+			Ok(v) => v as jdouble,
+			Err(e) => {
+				eprintln!("Failed to get RigidBody angular damping: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody angular damping: {}", e));
+				-1 as jdouble
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyAngularDamping(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		angular_damping: jdouble,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_angular_damping(&mut physics, world, rb_context, angular_damping as f64) {
+			eprintln!("Failed to set RigidBody angular damping: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody angular damping: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodySleep(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jboolean {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return 0 as jboolean;
+		};
+
+		match super::shared::get_rigidbody_sleep(physics, rb_context) {
+			Ok(v) => v as jboolean,
+			Err(e) => {
+				eprintln!("Failed to get RigidBody sleep state: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody sleep state: {}", e));
+				0 as jboolean
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodySleep(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		sleep: jboolean,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_sleep(&mut physics, world, rb_context, sleep != 0) {
+			eprintln!("Failed to set RigidBody sleep state: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody sleep state: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyCcdEnabled(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jboolean {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return 0 as jboolean;
+		};
+
+		match super::shared::get_rigidbody_ccd(physics, rb_context) {
+			Ok(v) => v as jboolean,
+			Err(e) => {
+				eprintln!("Failed to get RigidBody CCD state: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody CCD state: {}", e));
+				0 as jboolean
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyCcdEnabled(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		ccd_enabled: jboolean,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_ccd(&mut physics, world, rb_context, ccd_enabled != 0) {
+			eprintln!("Failed to set RigidBody CCD state: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody CCD state: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLinearVelocity(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jobject {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return std::ptr::null_mut();
+		};
+
+		match super::shared::get_rigidbody_linvel(physics, rb_context) {
+			Ok(v) => {
+				match v.to_jobject(&mut env) {
+					Ok(val) => val.into_raw(),
+					Err(e) => {
+						eprintln!("Failed to create Vector3d jobject: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d jobject: {}", e));
+						std::ptr::null_mut()
+					}
+				}
+			}
+			Err(e) => {
+				eprintln!("Failed to get RigidBody linear velocity: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody linear velocity: {}", e));
+				std::ptr::null_mut()
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLinearVelocity(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		linear_velocity: JObject,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		let Ok(velocity) = Vector3::from_jobject(&mut env, &linear_velocity) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/math/Vector3d to rust Vector3");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_linvel(&mut physics, world, rb_context, velocity) {
+			eprintln!("Failed to set RigidBody linear velocity: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody linear velocity: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyAngularVelocity(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jobject {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return std::ptr::null_mut();
+		};
+
+		match super::shared::get_rigidbody_angvel(physics, rb_context) {
+			Ok(v) => {
+				match v.to_jobject(&mut env) {
+					Ok(val) => val.into_raw(),
+					Err(e) => {
+						eprintln!("Failed to create Vector3d jobject: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d jobject: {}", e));
+						std::ptr::null_mut()
+					}
+				}
+			}
+			Err(e) => {
+				eprintln!("Failed to get RigidBody angular velocity: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody angular velocity: {}", e));
+				std::ptr::null_mut()
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyAngularVelocity(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		angular_velocity: JObject,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		let Ok(velocity) = Vector3::from_jobject(&mut env, &angular_velocity) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/math/Vector3d to rust Vector3");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_angvel(&mut physics, world, rb_context, velocity) {
+			eprintln!("Failed to set RigidBody angular velocity: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody angular velocity: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLockTranslation(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jobject {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return std::ptr::null_mut();
+		};
+
+		match super::shared::get_rigidbody_lock_translation(physics, rb_context) {
+			Ok(v) => {
+				match v.to_jobject(&mut env) {
+					Ok(val) => val.into_raw(),
+					Err(e) => {
+						eprintln!("Failed to create AxisLock jobject: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create AxisLock jobject: {}", e));
+						std::ptr::null_mut()
+					}
+				}
+			}
+			Err(e) => {
+				eprintln!("Failed to get RigidBody translation lock: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody translation lock: {}", e));
+				std::ptr::null_mut()
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLockTranslation(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		lock: JObject,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		let Ok(axis_lock) = AxisLock::from_jobject(&mut env, &lock) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/AxisLock to rust AxisLock");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_lock_translation(&mut physics, world, rb_context, axis_lock) {
+			eprintln!("Failed to set RigidBody translation lock: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody translation lock: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLockRotation(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jobject {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return std::ptr::null_mut();
+		};
+
+		match super::shared::get_rigidbody_lock_rotation(physics, rb_context) {
+			Ok(v) => {
+				match v.to_jobject(&mut env) {
+					Ok(val) => val.into_raw(),
+					Err(e) => {
+						eprintln!("Failed to create AxisLock jobject: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create AxisLock jobject: {}", e));
+						std::ptr::null_mut()
+					}
+				}
+			}
+			Err(e) => {
+				eprintln!("Failed to get RigidBody rotation lock: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody rotation lock: {}", e));
+				std::ptr::null_mut()
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLockRotation(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		lock: JObject,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		let Ok(axis_lock) = AxisLock::from_jobject(&mut env, &lock) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/AxisLock to rust AxisLock");
+			return;
+		};
+
+		if let Err(e) = super::shared::set_rigidbody_lock_rotation(&mut physics, world, rb_context, axis_lock) {
+			eprintln!("Failed to set RigidBody rotation lock: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody rotation lock: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyChildren(
+		mut env: JNIEnv,
+		_: JClass,
+		_world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+	) -> jobjectArray {
+		let physics = convert_ptr!(physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return std::ptr::null_mut();
+		};
+
+		match super::shared::get_rigidbody_children(physics, rb_context) {
+			Ok(children) => {
+				let mut handles: Vec<JObject> = Vec::new();
+				for c in children {
+					match IndexNative::from(c.0).to_jobject(&mut env) {
+						Ok(val) => { handles.push(val); }
+						Err(_) => { continue; }
+					}
+				}
+
+				let array = match env.new_object_array(handles.len() as i32, "com/dropbear/physics/Collider", JObject::null()) {
+					Ok(array) => array,
+					Err(e) => {
+						eprintln!("Failed to create jlong array: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create jlong array: {}", e));
+						return std::ptr::null_mut();
+					}
+				};
+
+				for (i, h) in handles.iter().enumerate() {
+					if let Err(e) = env.set_object_array_element(&array, i as jsize, h) {
+						eprintln!("Failed to set jlong array region: {}", e);
+						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to set jobject array region at index {}: {}", i, e));
+						return std::ptr::null_mut();
+					}
+				}
+
+				array.into_raw()
+			}
+			Err(e) => {
+				eprintln!("Failed to get RigidBody children: {}", e);
+				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody children: {}", e));
+				std::ptr::null_mut()
+			}
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_applyImpulse(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		x: jdouble,
+		y: jdouble,
+		z: jdouble,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		let impulse = Vector3::new(x as f64, y as f64, z as f64);
+		if let Err(e) = super::shared::apply_impulse(&mut physics, world, rb_context, impulse) {
+			eprintln!("Failed to apply impulse: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to apply impulse: {}", e));
+			return;
+		}
+	}
+
+	#[unsafe(no_mangle)]
+	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_applyTorqueImpulse(
+		mut env: JNIEnv,
+		_: JClass,
+		world_ptr: jlong,
+		physics_ptr: jlong,
+		rigidbody: JObject,
+		x: jdouble,
+		y: jdouble,
+		z: jdouble,
+	) {
+		let world = convert_ptr!(world_ptr => World);
+		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
+		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
+			let _ = env.throw_new("java/lang/RuntimeException",
+								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
+			return;
+		};
+
+		let torque = Vector3::new(x as f64, y as f64, z as f64);
+		if let Err(e) = super::shared::apply_torque_impulse(&mut physics, world, rb_context, torque) {
+			eprintln!("Failed to apply torque impulse: {}", e);
+			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to apply torque impulse: {}", e));
+			return;
+		}
+	}
 }
 
 #[dropbear_macro::impl_c_api]
diff --git a/eucalyptus-core/src/properties.rs b/eucalyptus-core/src/properties.rs
index 858d46d..4537985 100644
--- a/eucalyptus-core/src/properties.rs
+++ b/eucalyptus-core/src/properties.rs
@@ -221,7 +221,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_customPropertiesExistsForEntity(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_customPropertiesExistsForEntity(
         _env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -238,7 +238,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getStringProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getStringProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -258,7 +258,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getIntProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getIntProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -279,7 +279,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getLongProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getLongProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -300,7 +300,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getDoubleProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getDoubleProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -321,7 +321,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getFloatProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getFloatProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -342,7 +342,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getBoolProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getBoolProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -363,7 +363,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_getVec3Property(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_getVec3Property(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -386,7 +386,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setStringProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setStringProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -405,7 +405,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setIntProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setIntProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -423,7 +423,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setLongProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setLongProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -441,7 +441,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setFloatProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setFloatProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -459,7 +459,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setDoubleProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setDoubleProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -477,7 +477,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setBoolProperty(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setBoolProperty(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
@@ -495,7 +495,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_CustomPropertiesNative_setVec3Property(
+    pub extern "system" fn Java_com_com_dropbear_components_CustomPropertiesNative_setVec3Property(
         mut env: JNIEnv,
         _class: JClass,
         world_handle: jlong,
diff --git a/eucalyptus-core/src/scene/scripting.rs b/eucalyptus-core/src/scene/scripting.rs
index 5163e1b..bf22c06 100644
--- a/eucalyptus-core/src/scene/scripting.rs
+++ b/eucalyptus-core/src/scene/scripting.rs
@@ -1,12 +1,565 @@
+use crate::scripting::result::DropbearNativeResult;
+use ::jni::objects::{JObject, JValue};
+use ::jni::JNIEnv;
+
 pub mod shared {
+    use crate::command::CommandBuffer;
+    use crate::scene::loading::{SceneLoadResult, SceneLoader};
+    use crate::scripting::native::DropbearNativeError;
+    use crate::scripting::result::DropbearNativeResult;
+    use crossbeam_channel::Sender;
+    use parking_lot::Mutex;
+
+    pub fn load_scene_async(
+        command_buffer: &Sender<CommandBuffer>,
+        scene_loader: &Mutex<SceneLoader>,
+        scene_name: String,
+        _loading_scene: Option<String>,
+    ) -> DropbearNativeResult<u64> {
+        let mut loader = scene_loader.lock();
+
+        if let Some(existing_id) = loader.find_pending_id_by_name(&scene_name) {
+            return Ok(existing_id);
+        }
+
+        let id = loader.register_load(scene_name.clone());
+
+        let handle = crate::scene::loading::SceneLoadHandle {
+            id,
+            scene_name: scene_name.clone(),
+        };
+
+        // Send load command
+        command_buffer.try_send(CommandBuffer::LoadSceneAsync(handle))
+            .map_err(|_| DropbearNativeError::SendError)?;
+
+        Ok(id)
+    }
+
+    pub fn switch_to_scene_immediate(
+        command_buffer: &Sender<CommandBuffer>,
+        scene_name: String,
+    ) -> DropbearNativeResult<()> {
+        command_buffer.try_send(CommandBuffer::SwitchSceneImmediate(scene_name))
+            .map_err(|_| DropbearNativeError::SendError)?;
+        Ok(())
+    }
+
+    pub fn switch_to_scene_async(
+        command_buffer: &Sender<CommandBuffer>,
+        scene_loader: &Mutex<SceneLoader>,
+        scene_id: u64,
+    ) -> DropbearNativeResult<()> {
+        let loader = scene_loader.lock();
+
+        if let Some(entry) = loader.get_entry(scene_id) {
+            if matches!(entry.result, SceneLoadResult::Success) {
+                let handle = crate::scene::loading::SceneLoadHandle {
+                    id: scene_id,
+                    scene_name: entry.scene_name.clone(),
+                };
+
+                command_buffer.try_send(CommandBuffer::SwitchToAsync(handle))
+                    .map_err(|_| DropbearNativeError::SendError)?;
+                Ok(())
+            } else {
+                Err(DropbearNativeError::PrematureSceneSwitch)
+            }
+        } else {
+            Err(DropbearNativeError::NoSuchHandle)
+        }
+    }
+
+    pub fn get_scene_load_handle_scene_name(
+        scene_loader: &Mutex<SceneLoader>,
+        scene_id: u64,
+    ) -> DropbearNativeResult<String> {
+        let loader = scene_loader.lock();
+
+        if let Some(entry) = loader.get_entry(scene_id) {
+            Ok(entry.scene_name.clone())
+        } else {
+            Err(DropbearNativeError::NoSuchHandle)
+        }
+    }
+
+    pub fn get_scene_load_progress(
+        scene_loader: &Mutex<SceneLoader>,
+        scene_id: u64,
+    ) -> DropbearNativeResult<crate::utils::Progress> {
+        let mut loader = scene_loader.lock();
+
+        if let Some(entry) = loader.get_entry_mut(scene_id) {
+            // Update progress from status channel if available
+            if let Some(ref rx) = entry.status {
+                while let Ok(status) = rx.try_recv() {
+                    match status {
+                        crate::states::WorldLoadingStatus::Idle => {
+                            entry.progress.message = "Idle".to_string();
+                        },
+                        crate::states::WorldLoadingStatus::LoadingEntity { index, name, total } => {
+                            entry.progress.current = index;
+                            entry.progress.total = total;
+                            entry.progress.message = format!("Loading entity: {}", name);
+                        },
+                        crate::states::WorldLoadingStatus::Completed => {
+                            entry.progress.current = entry.progress.total;
+                            entry.progress.message = "Completed".to_string();
+                        }
+                    }
+                }
+            }
+
+            Ok(entry.progress.clone())
+        } else {
+            Err(DropbearNativeError::NoSuchHandle)
+        }
+    }
+
+    pub fn get_scene_load_status(
+        scene_loader: &Mutex<SceneLoader>,
+        scene_id: u64,
+    ) -> DropbearNativeResult<u32> {
+        let loader = scene_loader.lock();
 
+        if let Some(entry) = loader.get_entry(scene_id) {
+            let status = match entry.result {
+                SceneLoadResult::Pending => 0,  // PENDING
+                SceneLoadResult::Success => 1,  // READY
+                SceneLoadResult::Error(_) => 2, // FAILED
+            };
+            Ok(status)
+        } else {
+            Err(DropbearNativeError::NoSuchHandle)
+        }
+    }
 }
 
 pub mod jni {
+    #![allow(non_snake_case)]
+    use crate::command::CommandBuffer;
+    use crate::ptr::{CommandBufferPtr, SceneLoaderPtr};
+    use crate::scripting::jni::utils::ToJObject;
+    use crate::{convert_jstring, convert_ptr};
+    use jni::objects::{JClass, JString, JValue};
+    use jni::sys::{jint, jlong, jobject};
+    use jni::JNIEnv;
+
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneManagerNative_loadSceneAsyncNative__JJLjava_lang_String_2(
+        mut env: JNIEnv,
+        _: JClass,
+        command_buffer_ptr: jlong,
+        scene_manager_handle: jlong,
+        scene_name: JString,
+    ) -> jobject {
+        let command_buffer = convert_ptr!(command_buffer_ptr, CommandBufferPtr => crossbeam_channel::Sender<CommandBuffer>);
+        let scene_loader = convert_ptr!(scene_manager_handle, SceneLoaderPtr => parking_lot::Mutex<crate::scene::loading::SceneLoader>);
+
+        let scene_name_str = convert_jstring!(env, scene_name);
+
+        match super::shared::load_scene_async(command_buffer, scene_loader, scene_name_str, None) {
+            Ok(scene_id) => {
+                match env.find_class("java/lang/Long") {
+                    Ok(long_class) => {
+                        match env.new_object(long_class, "(J)V", &[JValue::Long(scene_id as i64)]) {
+                            Ok(obj) => obj.into_raw(),
+                            Err(e) => {
+                                eprintln!("Failed to create Long object: {}", e);
+                                let _ = env.throw_new("java/lang/RuntimeException",
+                                                      format!("Failed to create Long object: {}", e));
+                                std::ptr::null_mut()
+                            }
+                        }
+                    }
+                    Err(e) => {
+                        eprintln!("Failed to find Long class: {}", e);
+                        let _ = env.throw_new("java/lang/RuntimeException",
+                                              format!("Failed to find Long class: {}", e));
+                        std::ptr::null_mut()
+                    }
+                }
+            }
+            Err(e) => {
+                eprintln!("Failed to load scene async: {}", e);
+                let _ = env.throw_new("java/lang/RuntimeException",
+                                      format!("Failed to load scene async: {:?}", e));
+                std::ptr::null_mut()
+            }
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneManagerNative_loadSceneAsyncNative__JJLjava_lang_String_2Ljava_lang_String_2(
+        mut env: JNIEnv,
+        _: JClass,
+        command_buffer_ptr: jlong,
+        scene_manager_handle: jlong,
+        scene_name: JString,
+        loading_scene: JString,
+    ) -> jobject {
+        let command_buffer = convert_ptr!(command_buffer_ptr, CommandBufferPtr => crossbeam_channel::Sender<CommandBuffer>);
+        let scene_loader = convert_ptr!(scene_manager_handle, SceneLoaderPtr => parking_lot::Mutex<crate::scene::loading::SceneLoader>);
+
+        let scene_name_str = convert_jstring!(env, scene_name);
+        let loading_scene_str = convert_jstring!(env, loading_scene);
+
+        match super::shared::load_scene_async(command_buffer, scene_loader, scene_name_str, Some(loading_scene_str)) {
+            Ok(scene_id) => {
+                match env.find_class("java/lang/Long") {
+                    Ok(long_class) => {
+                        match env.new_object(long_class, "(J)V", &[JValue::Long(scene_id as i64)]) {
+                            Ok(obj) => obj.into_raw(),
+                            Err(e) => {
+                                eprintln!("Failed to create Long object: {}", e);
+                                let _ = env.throw_new("java/lang/RuntimeException",
+                                                      format!("Failed to create Long object: {}", e));
+                                std::ptr::null_mut()
+                            }
+                        }
+                    }
+                    Err(e) => {
+                        eprintln!("Failed to find Long class: {}", e);
+                        let _ = env.throw_new("java/lang/RuntimeException",
+                                              format!("Failed to find Long class: {}", e));
+                        std::ptr::null_mut()
+                    }
+                }
+            }
+            Err(e) => {
+                eprintln!("Failed to load scene async with loading scene: {}", e);
+                let _ = env.throw_new("java/lang/RuntimeException",
+                                      format!("Failed to load scene async: {:?}", e));
+                std::ptr::null_mut()
+            }
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneManagerNative_switchToSceneImmediateNative(
+        mut env: JNIEnv,
+        _: JClass,
+        command_buffer_ptr: jlong,
+        scene_name: JString,
+    ) {
+        let command_buffer = convert_ptr!(command_buffer_ptr, CommandBufferPtr => crossbeam_channel::Sender<CommandBuffer>);
+
+        let scene_name_str = convert_jstring!(env, scene_name);
+
+        if let Err(e) = super::shared::switch_to_scene_immediate(command_buffer, scene_name_str) {
+            eprintln!("Failed to switch scene immediate: {}", e);
+            let _ = env.throw_new("java/lang/RuntimeException",
+                                  format!("Failed to switch scene immediate: {:?}", e));
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneLoadHandleNative_getSceneLoadHandleSceneName(
+        mut env: JNIEnv,
+        _: JClass,
+        scene_loader_handle: jlong,
+        scene_id: jlong,
+    ) -> jobject {
+        let scene_loader = convert_ptr!(scene_loader_handle, SceneLoaderPtr => parking_lot::Mutex<crate::scene::loading::SceneLoader>);
+
+        match super::shared::get_scene_load_handle_scene_name(scene_loader, scene_id as u64) {
+            Ok(scene_name) => {
+                match env.new_string(scene_name) {
+                    Ok(jstring) => jstring.into_raw(),
+                    Err(e) => {
+                        eprintln!("Failed to create Java string: {}", e);
+                        let _ = env.throw_new("java/lang/RuntimeException",
+                                              format!("Failed to create Java string: {}", e));
+                        std::ptr::null_mut()
+                    }
+                }
+            }
+            Err(e) => {
+                eprintln!("Failed to get scene name: {}", e);
+                let _ = env.throw_new("java/lang/RuntimeException",
+                                      format!("Failed to get scene name: {:?}", e));
+                std::ptr::null_mut()
+            }
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneLoadHandleNative_switchToSceneAsync(
+        mut env: JNIEnv,
+        _: JClass,
+        command_buffer_ptr: jlong,
+        scene_id: jlong,
+    ) {
+        let command_buffer = convert_ptr!(command_buffer_ptr, CommandBufferPtr => crossbeam_channel::Sender<CommandBuffer>);
+        let scene_loader = convert_ptr!(scene_id as SceneLoaderPtr => parking_lot::Mutex<crate::scene::loading::SceneLoader>);
+
+        if let Err(e) = super::shared::switch_to_scene_async(command_buffer, scene_loader, scene_id as u64) {
+            eprintln!("Failed to switch scene async: {}", e);
+
+            // Check if it's a premature scene switch error
+            if let crate::scripting::native::DropbearNativeError::PrematureSceneSwitch = e {
+                let _ = env.throw_new("com/dropbear/exception/PrematureSceneSwitchException",
+                                      "Cannot switch to scene before it has finished loading");
+            } else {
+                let _ = env.throw_new("java/lang/RuntimeException",
+                                      format!("Failed to switch scene async: {:?}", e));
+            }
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneLoadHandleNative_getSceneLoadProgress(
+        mut env: JNIEnv,
+        _: JClass,
+        scene_loader_handle: jlong,
+        scene_id: jlong,
+    ) -> jobject {
+        let scene_loader = convert_ptr!(scene_loader_handle, SceneLoaderPtr => parking_lot::Mutex<crate::scene::loading::SceneLoader>);
+
+        match super::shared::get_scene_load_progress(scene_loader, scene_id as u64) {
+            Ok(progress) => {
+                match progress.to_jobject(&mut env) {
+                    Ok(obj) => obj.into_raw(),
+                    Err(e) => {
+                        eprintln!("Failed to create Progress object: {:?}", e);
+                        let _ = env.throw_new("java/lang/RuntimeException",
+                                              format!("Failed to create Progress object: {:?}", e));
+                        std::ptr::null_mut()
+                    }
+                }
+            }
+            Err(e) => {
+                eprintln!("Failed to get scene load progress: {}", e);
+                let _ = env.throw_new("java/lang/RuntimeException",
+                                      format!("Failed to get scene load progress: {:?}", e));
+                std::ptr::null_mut()
+            }
+        }
+    }
 
+    #[unsafe(no_mangle)]
+    pub extern "system" fn Java_com_dropbear_scene_SceneLoadHandleNative_getSceneLoadStatus(
+        mut env: JNIEnv,
+        _: JClass,
+        scene_loader_handle: jlong,
+        scene_id: jlong,
+    ) -> jint {
+        let scene_loader = convert_ptr!(scene_loader_handle, SceneLoaderPtr => parking_lot::Mutex<crate::scene::loading::SceneLoader>);
+
+        match super::shared::get_scene_load_status(scene_loader, scene_id as u64) {
+            Ok(status) => status as jint,
+            Err(e) => {
+                eprintln!("Failed to get scene load status: {}", e);
+                let _ = env.throw_new("java/lang/RuntimeException",
+                                      format!("Failed to get scene load status: {:?}", e));
+                -1 as jint
+            }
+        }
+    }
 }
 
-#[dropbear_macro::impl_c_api]
 pub mod native {
+    use crate::ptr::{CommandBufferPtr, SceneLoaderPtr};
+    use crate::scripting::native::DropbearNativeError;
+    use std::ffi::c_char;
+    use std::ffi::CStr;
+
+    #[unsafe(no_mangle)]
+    pub extern "C" fn load_scene_async(
+        command_buffer_ptr: CommandBufferPtr,
+        scene_loader_ptr: SceneLoaderPtr,
+        scene_name: *const c_char,
+        loading_scene: *const c_char,
+        out_scene_id: *mut u64,
+    ) -> DropbearNativeError {
+        if command_buffer_ptr.is_null() || scene_loader_ptr.is_null() || scene_name.is_null() {
+            return DropbearNativeError::NullPointer;
+        }
+
+        let command_buffer = unsafe { &*command_buffer_ptr };
+        let scene_loader = unsafe { &*scene_loader_ptr };
+
+        let scene_name_cstr = unsafe { CStr::from_ptr(scene_name) };
+        let scene_name_str = match scene_name_cstr.to_str() {
+            Ok(s) => s.to_string(),
+            Err(_) => return DropbearNativeError::InvalidUTF8,
+        };
+
+        let loading_scene_option = if !loading_scene.is_null() {
+            let loading_scene_cstr = unsafe { CStr::from_ptr(loading_scene) };
+            match loading_scene_cstr.to_str() {
+                Ok(s) => Some(s.to_string()),
+                Err(_) => return DropbearNativeError::InvalidUTF8,
+            }
+        } else {
+            None
+        };
+
+        match super::shared::load_scene_async(command_buffer, scene_loader, scene_name_str, loading_scene_option) {
+            Ok(scene_id) => {
+                if !out_scene_id.is_null() {
+                    unsafe { *out_scene_id = scene_id };
+                }
+                DropbearNativeError::Success
+            }
+            Err(e) => e,
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "C" fn switch_to_scene_immediate(
+        command_buffer_ptr: CommandBufferPtr,
+        scene_name: *const c_char,
+    ) -> DropbearNativeError {
+        if command_buffer_ptr.is_null() || scene_name.is_null() {
+            return DropbearNativeError::NullPointer;
+        }
+
+        let command_buffer = unsafe { &*command_buffer_ptr };
+
+        let scene_name_cstr = unsafe { CStr::from_ptr(scene_name) };
+        let scene_name_str = match scene_name_cstr.to_str() {
+            Ok(s) => s.to_string(),
+            Err(_) => return DropbearNativeError::InvalidUTF8,
+        };
+
+        match super::shared::switch_to_scene_immediate(command_buffer, scene_name_str) {
+            Ok(_) => DropbearNativeError::Success,
+            Err(e) => e,
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "C" fn get_scene_load_handle_scene_name(
+        scene_loader_ptr: SceneLoaderPtr,
+        scene_id: u64,
+        out_buffer: *mut c_char,
+        buffer_size: usize,
+    ) -> DropbearNativeError {
+        if scene_loader_ptr.is_null() || out_buffer.is_null() {
+            return DropbearNativeError::NullPointer;
+        }
+
+        let scene_loader = unsafe { &*scene_loader_ptr };
+
+        match super::shared::get_scene_load_handle_scene_name(scene_loader, scene_id) {
+            Ok(scene_name) => {
+                let c_string = match std::ffi::CString::new(scene_name) {
+                    Ok(cstr) => cstr,
+                    Err(_) => return DropbearNativeError::CStringError,
+                };
+
+                let bytes = c_string.as_bytes_with_nul();
+                if bytes.len() > buffer_size {
+                    return DropbearNativeError::BufferTooSmall;
+                }
+
+                unsafe {
+                    std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_buffer as *mut u8, bytes.len());
+                }
+
+                DropbearNativeError::Success
+            }
+            Err(e) => e,
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "C" fn switch_to_scene_async(
+        command_buffer_ptr: CommandBufferPtr,
+        scene_loader_ptr: SceneLoaderPtr,
+        scene_id: u64,
+    ) -> DropbearNativeError {
+        if command_buffer_ptr.is_null() || scene_loader_ptr.is_null() {
+            return DropbearNativeError::NullPointer;
+        }
+
+        let command_buffer = unsafe { &*command_buffer_ptr };
+        let scene_loader = unsafe { &*scene_loader_ptr };
+
+        match super::shared::switch_to_scene_async(command_buffer, scene_loader, scene_id) {
+            Ok(_) => DropbearNativeError::Success,
+            Err(e) => e,
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "C" fn get_scene_load_progress(
+        scene_loader_ptr: SceneLoaderPtr,
+        scene_id: u64,
+        out_current: *mut f64,
+        out_total: *mut f64,
+        out_message: *mut c_char,
+        message_buffer_size: usize,
+    ) -> DropbearNativeError {
+        if scene_loader_ptr.is_null() {
+            return DropbearNativeError::NullPointer;
+        }
+
+        let scene_loader = unsafe { &*scene_loader_ptr };
+
+        match super::shared::get_scene_load_progress(scene_loader, scene_id) {
+            Ok(progress) => {
+                if !out_current.is_null() {
+                    unsafe { *out_current = progress.current as f64 };
+                }
+                if !out_total.is_null() {
+                    unsafe { *out_total = progress.total as f64 };
+                }
+
+                if !out_message.is_null() && message_buffer_size > 0 {
+                    let message_cstr = match std::ffi::CString::new(progress.message) {
+                        Ok(cstr) => cstr,
+                        Err(_) => return DropbearNativeError::CStringError,
+                    };
+
+                    let bytes = message_cstr.as_bytes_with_nul();
+                    let copy_len = std::cmp::min(bytes.len(), message_buffer_size);
+
+                    unsafe {
+                        std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_message as *mut u8, copy_len);
+                    }
+                }
+
+                DropbearNativeError::Success
+            }
+            Err(e) => e,
+        }
+    }
+
+    #[unsafe(no_mangle)]
+    pub extern "C" fn get_scene_load_status(
+        scene_loader_ptr: SceneLoaderPtr,
+        scene_id: u64,
+    ) -> i32 {
+        if scene_loader_ptr.is_null() {
+            return DropbearNativeError::NullPointer as i32;
+        }
+
+        let scene_loader = unsafe { &*scene_loader_ptr };
+
+        match super::shared::get_scene_load_status(scene_loader, scene_id) {
+            Ok(status) => status as i32,
+            Err(e) => e as i32,
+        }
+    }
+}
+
+impl crate::scripting::jni::utils::ToJObject for crate::utils::Progress {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/utils/Progress")
+            .map_err(|_| crate::scripting::native::DropbearNativeError::JNIClassNotFound)?;
+
+        let message_jstring = env.new_string(&self.message)
+            .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?;
+
+        let obj = env.new_object(&class, "(DDLjava/lang/String;)V", &[
+            JValue::Double(self.current as f64),
+            JValue::Double(self.total as f64),
+            JValue::Object(&JObject::from(message_jstring)),
+        ])
+            .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?;
 
+        Ok(obj)
+    }
 }
\ No newline at end of file
diff --git a/eucalyptus-core/src/scripting.rs b/eucalyptus-core/src/scripting.rs
index 6efd977..3f9d986 100644
--- a/eucalyptus-core/src/scripting.rs
+++ b/eucalyptus-core/src/scripting.rs
@@ -14,7 +14,7 @@ use std::sync::OnceLock;
 use crate::ptr::{AssetRegistryPtr, CommandBufferPtr, InputStatePtr, PhysicsStatePtr, SceneLoaderPtr, WorldPtr};
 use crate::scripting::jni::JavaContext;
 use crate::scripting::native::NativeLibrary;
-use crate::states::Script;
+use crate::states::{Script};
 use anyhow::Context;
 use crossbeam_channel::Sender;
 use dropbear_engine::asset::ASSET_REGISTRY;
diff --git a/eucalyptus-core/src/scripting/jni.rs b/eucalyptus-core/src/scripting/jni.rs
index 419d4ea..f332732 100644
--- a/eucalyptus-core/src/scripting/jni.rs
+++ b/eucalyptus-core/src/scripting/jni.rs
@@ -8,9 +8,9 @@ use crate::APP_INFO;
 use crate::logging::LOG_LEVEL;
 use crate::ptr::WorldPtr;
 use crate::scripting::error::LastErrorMessage;
-use jni::objects::{GlobalRef, JClass, JLongArray, JObject, JValue};
+use jni::objects::{GlobalRef, JClass, JLongArray, JObject, JObjectArray, JString, JValue};
 use jni::sys::jlong;
-use jni::{InitArgsBuilder, JNIVersion, JavaVM};
+use jni::{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM};
 use sha2::{Digest, Sha256};
 use std::fs;
 use std::net::TcpListener;
@@ -295,109 +295,184 @@ impl JavaContext {
     ) -> anyhow::Result<()> {
         let mut env = self.jvm.attach_current_thread()?;
 
-        let world_handle = context.world as jlong;
-        let input_handle = context.input as jlong;
-        let graphics_handle = context.graphics as jlong;
-        let asset_handle = context.assets as jlong;
-        let scene_loader_handle = context.scene_loader as jlong;
-        let physics_handle = context.physics_state as jlong;
-
-        let dropbear_context_class: JClass = env.find_class("com/dropbear/ffi/DropbearContext")?;
-        let dropbear_context_obj = env.new_object(
-            dropbear_context_class,
-            "(JJJJJ)V",
-            &[
-                JValue::Long(world_handle),
-                JValue::Long(input_handle),
-                JValue::Long(graphics_handle),
-                JValue::Long(asset_handle),
-                JValue::Long(scene_loader_handle),
-                JValue::Long(physics_handle)
-            ]
-        )?;
-
-        log::trace!("Locating \"com/dropbear/ffi/NativeEngine\" class");
-        let native_engine_class: JClass = env.find_class("com/dropbear/ffi/NativeEngine")?;
-
-        let native_engine_obj = if let Some(ref native_engine_ref) = self.native_engine_instance {
-            native_engine_ref.as_obj()
-        } else {
-            log::trace!("Creating new instance of NativeEngine");
-            let native_engine_obj = env.new_object(native_engine_class, "()V", &[])?;
-            let native_engine_global_ref = env.new_global_ref(native_engine_obj)?;
-            self.native_engine_instance = Some(native_engine_global_ref);
-            self.native_engine_instance
-                .as_ref()
-                .expect("NativeEngine global ref must exist")
-                .as_obj()
-        };
+        let result = (|| -> anyhow::Result<()> {
+            let world_handle = context.world as jlong;
+            let input_handle = context.input as jlong;
+            let graphics_handle = context.graphics as jlong;
+            let asset_handle = context.assets as jlong;
+            let scene_loader_handle = context.scene_loader as jlong;
+            let physics_handle = context.physics_state as jlong;
+
+            let dropbear_context_class: JClass = env.find_class("com/dropbear/ffi/DropbearContext")?;
+            let dropbear_context_obj = env.new_object(
+                dropbear_context_class,
+                "(JJJJJ)V",
+                &[
+                    JValue::Long(world_handle),
+                    JValue::Long(input_handle),
+                    JValue::Long(graphics_handle),
+                    JValue::Long(asset_handle),
+                    JValue::Long(scene_loader_handle),
+                    JValue::Long(physics_handle)
+                ]
+            )?;
 
-        log::trace!("Calling NativeEngine.init() with arg [\"com/dropbear/ffi/DropbearContext\"]");
-        env.call_method(
-            native_engine_obj,
-            "init",
-            "(Lcom/dropbear/ffi/DropbearContext;)V",
-            &[JValue::Object(&dropbear_context_obj)],
-        )?;
-
-        if self.dropbear_engine_class.is_none() {
-            let dropbear_class: JClass = env.find_class("com/dropbear/DropbearEngine")?;
-            log::trace!("Creating DropbearEngine constructor with arg (NativeEngine_object)");
-            let dropbear_obj = env.new_object(
-                dropbear_class,
-                "(Lcom/dropbear/ffi/NativeEngine;)V",
-                &[JValue::Object(native_engine_obj)],
+            log::trace!("Locating \"com/dropbear/ffi/NativeEngine\" class");
+            let native_engine_class: JClass = env.find_class("com/dropbear/ffi/NativeEngine")?;
+
+            let native_engine_obj = if let Some(ref native_engine_ref) = self.native_engine_instance {
+                native_engine_ref.as_obj()
+            } else {
+                log::trace!("Creating new instance of NativeEngine");
+                let native_engine_obj = env.new_object(native_engine_class, "()V", &[])?;
+                let native_engine_global_ref = env.new_global_ref(native_engine_obj)?;
+                self.native_engine_instance = Some(native_engine_global_ref);
+                self.native_engine_instance
+                    .as_ref()
+                    .expect("NativeEngine global ref must exist")
+                    .as_obj()
+            };
+
+            log::trace!("Calling NativeEngine.init() with arg [\"com/dropbear/ffi/DropbearContext\"]");
+            env.call_method(
+                native_engine_obj,
+                "init",
+                "(Lcom/dropbear/ffi/DropbearContext;)V",
+                &[JValue::Object(&dropbear_context_obj)],
             )?;
 
-            log::trace!("Creating new global ref for DropbearEngine");
-            let engine_global_ref = env.new_global_ref(dropbear_obj)?;
-            self.dropbear_engine_class = Some(engine_global_ref);
-        }
+            if self.dropbear_engine_class.is_none() {
+                let dropbear_class: JClass = env.find_class("com/dropbear/DropbearEngine")?;
+                log::trace!("Creating DropbearEngine constructor with arg (NativeEngine_object)");
+                let dropbear_obj = env.new_object(
+                    dropbear_class,
+                    "(Lcom/dropbear/ffi/NativeEngine;)V",
+                    &[JValue::Object(native_engine_obj)],
+                )?;
+
+                log::trace!("Creating new global ref for DropbearEngine");
+                let engine_global_ref = env.new_global_ref(dropbear_obj)?;
+                self.dropbear_engine_class = Some(engine_global_ref);
+            }
 
-        let jar_path_jstring = env.new_string(self.jar_path.to_string_lossy())?;
-        let log_level_str = { LOG_LEVEL.lock().to_string() };
-        let log_level_enum_class = env.find_class("com/dropbear/logging/LogLevel")?;
-        let log_level_enum_instance = env
-            .get_static_field(
-                log_level_enum_class,
-                log_level_str,
-                "Lcom/dropbear/logging/LogLevel;",
-            )?
-            .l()?;
-
-        let std_out_writer_class = env.find_class("com/dropbear/logging/StdoutWriter")?;
-        let log_writer_obj = env.new_object(std_out_writer_class, "()V", &[])?;
-
-        if self.system_manager_instance.is_none() {
-            let engine_ref = self
-                .dropbear_engine_class
-                .as_ref()
-                .expect("DropbearEngine global ref must exist")
-                .as_obj();
-
-            log::trace!("Locating \"com/dropbear/host/SystemManager\" class");
-            let system_manager_class: JClass = env.find_class("com/dropbear/host/SystemManager")?;
-            log::trace!(
+            let jar_path_jstring = env.new_string(self.jar_path.to_string_lossy())?;
+            let log_level_str = { LOG_LEVEL.lock().to_string() };
+            let log_level_enum_class = env.find_class("com/dropbear/logging/LogLevel")?;
+            let log_level_enum_instance = env
+                .get_static_field(
+                    log_level_enum_class,
+                    log_level_str,
+                    "Lcom/dropbear/logging/LogLevel;",
+                )?
+                .l()?;
+
+            let std_out_writer_class = env.find_class("com/dropbear/logging/StdoutWriter")?;
+            let log_writer_obj = env.new_object(std_out_writer_class, "()V", &[])?;
+
+            if self.system_manager_instance.is_none() {
+                let engine_ref = self
+                    .dropbear_engine_class
+                    .as_ref()
+                    .expect("DropbearEngine global ref must exist")
+                    .as_obj();
+
+                log::trace!("Locating \"com/dropbear/host/SystemManager\" class");
+                let system_manager_class: JClass = env.find_class("com/dropbear/host/SystemManager")?;
+                log::trace!(
                 "Creating SystemManager constructor with args (jar_path_string, dropbear_engine_object, log_writer_object, log_level_enum, log_target_string)"
             );
 
-            let log_target_jstring = env.new_string("dropbear_rust_host")?;
+                let log_target_jstring = env.new_string("dropbear_rust_host")?;
+
+                let system_manager_obj = env.new_object(
+                    system_manager_class,
+                    "(Ljava/lang/String;Lcom/dropbear/DropbearEngine;Lcom/dropbear/logging/LogWriter;Lcom/dropbear/logging/LogLevel;Ljava/lang/String;)V",
+                    &[
+                        JValue::Object(&jar_path_jstring),
+                        JValue::Object(engine_ref),
+                        JValue::Object(&log_writer_obj),
+                        JValue::Object(&log_level_enum_instance),
+                        JValue::Object(&log_target_jstring),
+                    ],
+                )?;
 
-            let system_manager_obj = env.new_object(
-                system_manager_class,
-                "(Ljava/lang/String;Lcom/dropbear/DropbearEngine;Lcom/dropbear/logging/LogWriter;Lcom/dropbear/logging/LogLevel;Ljava/lang/String;)V",
-                &[
-                    JValue::Object(&jar_path_jstring),
-                    JValue::Object(engine_ref),
-                    JValue::Object(&log_writer_obj),
-                    JValue::Object(&log_level_enum_instance),
-                    JValue::Object(&log_target_jstring),
-                ],
+                log::trace!("Creating new global ref for SystemManager");
+                let manager_global_ref = env.new_global_ref(system_manager_obj)?;
+                self.system_manager_instance = Some(manager_global_ref);
+            }
+
+            Ok(())
+        })();
+
+        Self::get_exception(&mut env)?;
+
+        result
+    }
+
+    pub fn get_exception(env: &mut JNIEnv) -> anyhow::Result<()> {
+        if let Ok(ex) = env.exception_occurred() {
+            env.exception_clear()?;
+
+            let message_result = env.call_method(
+                &ex,
+                "toString",
+                "()Ljava/lang/String;",
+                &[]
+            )?;
+            let message_obj = message_result.l()?;
+            let message_jstring = JString::from(message_obj);
+            let message_str: String = env.get_string(&message_jstring)?.into();
+
+            let stack_trace_result = env.call_method(
+                &ex,
+                "getStackTrace",
+                "()[Ljava/lang/StackTraceElement;",
+                &[]
+            )?;
+            let stack_trace_obj = stack_trace_result.l()?;
+            let stack_trace_array = JObjectArray::from(stack_trace_obj);
+            let stack_len = env.get_array_length(&stack_trace_array)?;
+
+            let mut error_msg = format!("{}\n", message_str);
+
+            for i in 0..stack_len {
+                let element = env.get_object_array_element(&stack_trace_array, i)?;
+
+                let element_str_result = env.call_method(
+                    &element,
+                    "toString",
+                    "()Ljava/lang/String;",
+                    &[]
+                )?;
+                let element_str_obj = element_str_result.l()?;
+                let element_jstring = JString::from(element_str_obj);
+                let element_string: String = env.get_string(&element_jstring)?.into();
+
+                error_msg.push_str(&format!("  at {}\n", element_string));
+            }
+
+            let cause_result = env.call_method(
+                &ex,
+                "getCause",
+                "()Ljava/lang/Throwable;",
+                &[]
             )?;
+            let cause_obj = cause_result.l()?;
+
+            if !cause_obj.is_null() {
+                let cause_str_result = env.call_method(
+                    &cause_obj,
+                    "toString",
+                    "()Ljava/lang/String;",
+                    &[]
+                )?;
+                let cause_str_obj = cause_str_result.l()?;
+                let cause_jstring = JString::from(cause_str_obj);
+                let cause_string: String = env.get_string(&cause_jstring)?.into();
+                error_msg.push_str(&format!("Caused by: {}\n", cause_string));
+            }
 
-            log::trace!("Creating new global ref for SystemManager");
-            let manager_global_ref = env.new_global_ref(system_manager_obj)?;
-            self.system_manager_instance = Some(manager_global_ref);
+            return Err(anyhow::anyhow!("Java exception: {}", error_msg));
         }
 
         Ok(())
@@ -412,85 +487,107 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!("Calling SystemManager.reloadJar()");
-            let jar_path_jstring = env.new_string(self.jar_path.to_string_lossy())?;
-            env.call_method(
-                manager_ref,
-                "reloadJar",
-                "(Ljava/lang/String;)V",
-                &[JValue::Object(&jar_path_jstring)],
-            )?;
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!("Calling SystemManager.reloadJar()");
+                let jar_path_jstring = env.new_string(self.jar_path.to_string_lossy())?;
+                env.call_method(
+                    manager_ref,
+                    "reloadJar",
+                    "(Ljava/lang/String;)V",
+                    &[JValue::Object(&jar_path_jstring)],
+                )?;
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             log::warn!("SystemManager instance not found during reload.");
             // self.init(world)?;
             return Err(anyhow::anyhow!("SystemManager not initialised for reload."));
         }
-
-        log::info!("Reload complete via SystemManager!");
-
-        Ok(())
     }
 
     pub fn load_systems_for_tag(&mut self, tag: &str) -> anyhow::Result<()> {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!(
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!(
                 "Calling SystemManager.loadSystemsForTag() with tag: {}",
                 tag
             );
-            let tag_jstring = env.new_string(tag)?;
-            env.call_method(
-                manager_ref,
-                "loadSystemsForTag",
-                "(Ljava/lang/String;)V",
-                &[JValue::Object(&tag_jstring)],
-            )?;
+                let tag_jstring = env.new_string(tag)?;
+                env.call_method(
+                    manager_ref,
+                    "loadSystemsForTag",
+                    "(Ljava/lang/String;)V",
+                    &[JValue::Object(&tag_jstring)],
+                )?;
+
+                log::debug!("Loaded systems for tag: {}", tag);
+                Ok(())
+            })();
 
-            log::debug!("Loaded systems for tag: {}", tag);
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             return Err(anyhow::anyhow!(
                 "SystemManager not initialised when loading systems for tag: {}",
                 tag
             ));
         }
-        Ok(())
     }
 
     pub fn update_all_systems(&self, dt: f32) -> anyhow::Result<()> {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log_once::trace_once!("Calling SystemManager.updateAllSystems() with dt: {}", dt);
-            env.call_method(
-                manager_ref,
-                "updateAllSystems",
-                "(F)V",
-                &[JValue::Float(dt)],
-            )?;
+            let result = (|| -> anyhow::Result<()> {
+                log_once::trace_once!("Calling SystemManager.updateAllSystems() with dt: {}", dt);
+                env.call_method(
+                    manager_ref,
+                    "updateAllSystems",
+                    "(F)V",
+                    &[JValue::Float(dt)],
+                )?;
+
+                log_once::trace_once!("Updated all systems with dt: {}", dt);
 
-            log_once::trace_once!("Updated all systems with dt: {}", dt);
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             return Err(anyhow::anyhow!(
                 "SystemManager not initialised when updating systems."
             ));
         }
-        Ok(())
     }
 
     pub fn physics_update_all_systems(&self, dt: f32) -> anyhow::Result<()> {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log_once::trace_once!("Calling SystemManager.physicsUpdateAllSystems() with dt: {}", dt);
-            env.call_method(
-                manager_ref,
-                "physicsUpdateAllSystems",
-                "(F)V",
-                &[JValue::Float(dt)],
-            )?;
+            let result = (|| -> anyhow::Result<()> {
+                log_once::trace_once!("Calling SystemManager.physicsUpdateAllSystems() with dt: {}", dt);
+                env.call_method(
+                    manager_ref,
+                    "physicsUpdateAllSystems",
+                    "(F)V",
+                    &[JValue::Float(dt)],
+                )?;
 
-            Ok(())
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when physics updating systems."
@@ -502,47 +599,59 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!(
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!(
                 "Calling SystemManager.updateSystemsByTag() with tag: {}, dt: {}",
                 tag,
                 dt
             );
-            let tag_jstring = env.new_string(tag)?;
-            env.call_method(
-                manager_ref,
-                "updateSystemsByTag",
-                "(Ljava/lang/String;F)V",
-                &[JValue::Object(&tag_jstring), JValue::Float(dt)],
-            )?;
+                let tag_jstring = env.new_string(tag)?;
+                env.call_method(
+                    manager_ref,
+                    "updateSystemsByTag",
+                    "(Ljava/lang/String;F)V",
+                    &[JValue::Object(&tag_jstring), JValue::Float(dt)],
+                )?;
 
-            log::debug!("Updated systems for tag: {} with dt: {}", tag, dt);
+                log::debug!("Updated systems for tag: {} with dt: {}", tag, dt);
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             return Err(anyhow::anyhow!(
                 "SystemManager not initialised when updating systems for tag: {}",
                 tag
             ));
         }
-        Ok(())
     }
 
     pub fn physics_update_systems_for_tag(&self, tag: &str, dt: f32) -> anyhow::Result<()> {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!(
-                "Calling SystemManager.physicsUpdateSystemsByTag() with tag: {}, dt: {}",
-                tag,
-                dt
-            );
-            let tag_jstring = env.new_string(tag)?;
-            env.call_method(
-                manager_ref,
-                "physicsUpdateSystemsByTag",
-                "(Ljava/lang/String;F)V",
-                &[JValue::Object(&tag_jstring), JValue::Float(dt)],
-            )?;
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!(
+                    "Calling SystemManager.physicsUpdateSystemsByTag() with tag: {}, dt: {}",
+                    tag,
+                    dt
+                );
+                let tag_jstring = env.new_string(tag)?;
+                env.call_method(
+                    manager_ref,
+                    "physicsUpdateSystemsByTag",
+                    "(Ljava/lang/String;F)V",
+                    &[JValue::Object(&tag_jstring), JValue::Float(dt)],
+                )?;
 
-            Ok(())
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when physics updating systems for tag: {}",
@@ -560,49 +669,55 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!(
-                "Calling SystemManager.updateSystemsForEntities() with tag: {}, count: {}, dt: {}",
-                tag,
-                entity_ids.len(),
-                dt
-            );
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!(
+                    "Calling SystemManager.updateSystemsForEntities() with tag: {}, count: {}, dt: {}",
+                    tag,
+                    entity_ids.len(),
+                    dt
+                );
 
-            let tag_jstring = env.new_string(tag)?;
-            let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?;
-            let entity_array_raw = entity_array.as_raw();
-            log::trace!("u64 entity: {:?}", entity_ids);
-            log::trace!(
-                "i64 entity: {:?}",
-                entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>()
-            );
-            if !entity_ids.is_empty() {
-                env.set_long_array_region(
-                    entity_array,
-                    0,
-                    &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(),
+                let tag_jstring = env.new_string(tag)?;
+                let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?;
+                let entity_array_raw = entity_array.as_raw();
+                log::trace!("u64 entity: {:?}", entity_ids);
+                log::trace!(
+                    "i64 entity: {:?}",
+                    entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>()
+                );
+                if !entity_ids.is_empty() {
+                    env.set_long_array_region(
+                        entity_array,
+                        0,
+                        &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(),
+                    )?;
+                }
+                let entity_array_obj =
+                    unsafe { JObject::from_raw(entity_array_raw.cast::<jni::sys::_jobject>()) };
+
+                env.call_method(
+                    manager_ref,
+                    "updateSystemsForEntities",
+                    "(Ljava/lang/String;[JF)V",
+                    &[
+                        JValue::Object(&tag_jstring),
+                        JValue::Object(&entity_array_obj),
+                        JValue::Float(dt),
+                    ],
                 )?;
-            }
-            let entity_array_obj =
-                unsafe { JObject::from_raw(entity_array_raw.cast::<jni::sys::_jobject>()) };
 
-            env.call_method(
-                manager_ref,
-                "updateSystemsForEntities",
-                "(Ljava/lang/String;[JF)V",
-                &[
-                    JValue::Object(&tag_jstring),
-                    JValue::Object(&entity_array_obj),
-                    JValue::Float(dt),
-                ],
-            )?;
+                log::trace!(
+                    "Updated systems for tag: {} across {} entities with dt: {}",
+                    tag,
+                    entity_ids.len(),
+                    dt
+                );
+                Ok(())
+            })();
 
-            log::trace!(
-                "Updated systems for tag: {} across {} entities with dt: {}",
-                tag,
-                entity_ids.len(),
-                dt
-            );
-            Ok(())
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when updating systems for tag: {}",
@@ -620,38 +735,44 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!(
-                "Calling SystemManager.physicsUpdateSystemsForEntities() with tag: {}, count: {}, dt: {}",
-                tag,
-                entity_ids.len(),
-                dt
-            );
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!(
+                    "Calling SystemManager.physicsUpdateSystemsForEntities() with tag: {}, count: {}, dt: {}",
+                    tag,
+                    entity_ids.len(),
+                    dt
+                );
 
-            let tag_jstring = env.new_string(tag)?;
-            let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?;
-            let entity_array_raw = entity_array.as_raw();
-            if !entity_ids.is_empty() {
-                env.set_long_array_region(
-                    entity_array,
-                    0,
-                    &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(),
+                let tag_jstring = env.new_string(tag)?;
+                let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?;
+                let entity_array_raw = entity_array.as_raw();
+                if !entity_ids.is_empty() {
+                    env.set_long_array_region(
+                        entity_array,
+                        0,
+                        &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(),
+                    )?;
+                }
+                let entity_array_obj =
+                    unsafe { JObject::from_raw(entity_array_raw.cast::<jni::sys::_jobject>()) };
+
+                env.call_method(
+                    manager_ref,
+                    "physicsUpdateSystemsForEntities",
+                    "(Ljava/lang/String;[JF)V",
+                    &[
+                        JValue::Object(&tag_jstring),
+                        JValue::Object(&entity_array_obj),
+                        JValue::Float(dt),
+                    ],
                 )?;
-            }
-            let entity_array_obj =
-                unsafe { JObject::from_raw(entity_array_raw.cast::<jni::sys::_jobject>()) };
 
-            env.call_method(
-                manager_ref,
-                "physicsUpdateSystemsForEntities",
-                "(Ljava/lang/String;[JF)V",
-                &[
-                    JValue::Object(&tag_jstring),
-                    JValue::Object(&entity_array_obj),
-                    JValue::Float(dt),
-                ],
-            )?;
+                Ok(())
+            })();
 
-            Ok(())
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when physics updating systems for tag: {}",
@@ -664,16 +785,22 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!("Calling SystemManager.unloadSystemsByTag() with tag: {}", tag);
-            let tag_jstring = env.new_string(tag)?;
-            env.call_method(
-                manager_ref,
-                "unloadSystemsByTag",
-                "(Ljava/lang/String;)V",
-                &[JValue::Object(&tag_jstring)],
-            )?;
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!("Calling SystemManager.unloadSystemsByTag() with tag: {}", tag);
+                let tag_jstring = env.new_string(tag)?;
+                env.call_method(
+                    manager_ref,
+                    "unloadSystemsByTag",
+                    "(Ljava/lang/String;)V",
+                    &[JValue::Object(&tag_jstring)],
+                )?;
 
-            Ok(())
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when unloading systems for tag: {}",
@@ -686,19 +813,25 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!(
-                "Calling SystemManager.destroySystemsByTag() with tag: {}",
-                tag
-            );
-            let tag_jstring = env.new_string(tag)?;
-            env.call_method(
-                manager_ref,
-                "destroySystemsByTag",
-                "(Ljava/lang/String;)V",
-                &[JValue::Object(&tag_jstring)],
-            )?;
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!(
+                    "Calling SystemManager.destroySystemsByTag() with tag: {}",
+                    tag
+                );
+                let tag_jstring = env.new_string(tag)?;
+                env.call_method(
+                    manager_ref,
+                    "destroySystemsByTag",
+                    "(Ljava/lang/String;)V",
+                    &[JValue::Object(&tag_jstring)],
+                )?;
 
-            Ok(())
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when destroying systems for tag: {}",
@@ -711,9 +844,15 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!("Calling SystemManager.unloadAllSystems()");
-            env.call_method(manager_ref, "unloadAllSystems", "()V", &[])?;
-            Ok(())
+            let result = (|| -> anyhow::Result<()> {
+                log::trace!("Calling SystemManager.unloadAllSystems()");
+                env.call_method(manager_ref, "unloadAllSystems", "()V", &[])?;
+                Ok(())
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when unloading all systems."
@@ -725,16 +864,22 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!("Calling SystemManager.getSystemCount() for tag: {}", tag);
-            let tag_jstring = env.new_string(tag)?;
-            let result = env.call_method(
-                manager_ref,
-                "getSystemCount",
-                "(Ljava/lang/String;)I",
-                &[JValue::Object(&tag_jstring)],
-            )?;
+            let result = (|| -> anyhow::Result<i32> {
+                log::trace!("Calling SystemManager.getSystemCount() for tag: {}", tag);
+                let tag_jstring = env.new_string(tag)?;
+                let result = env.call_method(
+                    manager_ref,
+                    "getSystemCount",
+                    "(Ljava/lang/String;)I",
+                    &[JValue::Object(&tag_jstring)],
+                )?;
 
-            Ok(result.i()?)
+                Ok(result.i()?)
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when getting system count for tag: {}",
@@ -747,16 +892,22 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!("Calling SystemManager.hasSystemsForTag() for tag: {}", tag);
-            let tag_jstring = env.new_string(tag)?;
-            let result = env.call_method(
-                manager_ref,
-                "hasSystemsForTag",
-                "(Ljava/lang/String;)Z",
-                &[JValue::Object(&tag_jstring)],
-            )?;
+            let result = (|| -> anyhow::Result<bool> {
+                log::trace!("Calling SystemManager.hasSystemsForTag() for tag: {}", tag);
+                let tag_jstring = env.new_string(tag)?;
+                let result = env.call_method(
+                    manager_ref,
+                    "hasSystemsForTag",
+                    "(Ljava/lang/String;)Z",
+                    &[JValue::Object(&tag_jstring)],
+                )?;
 
-            Ok(result.z()?)
+                Ok(result.z()?)
+            })();
+
+            Self::get_exception(&mut env)?;
+
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when checking for systems for tag: {}",
@@ -769,10 +920,16 @@ impl JavaContext {
         if let Some(ref manager_ref) = self.system_manager_instance {
             let mut env = self.jvm.attach_current_thread()?;
 
-            log::trace!("Calling SystemManager.getTotalSystemCount()");
-            let result = env.call_method(manager_ref, "getTotalSystemCount", "()I", &[])?;
+            let result = (|| -> anyhow::Result<i32> {
+                log::trace!("Calling SystemManager.getTotalSystemCount()");
+                let result = env.call_method(manager_ref, "getTotalSystemCount", "()I", &[])?;
+
+                Ok(result.i()?)
+            })();
+
+            Self::get_exception(&mut env)?;
 
-            Ok(result.i()?)
+            Ok(result?)
         } else {
             Err(anyhow::anyhow!(
                 "SystemManager not initialised when getting total system count."
diff --git a/eucalyptus-core/src/scripting/native.rs b/eucalyptus-core/src/scripting/native.rs
index f54f277..9cb00c9 100644
--- a/eucalyptus-core/src/scripting/native.rs
+++ b/eucalyptus-core/src/scripting/native.rs
@@ -350,6 +350,7 @@ impl LastErrorMessage for NativeLibrary {
     }
 }
 
+#[repr(C)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 /// Displays the types of errors that can be returned by the native library.
 pub enum DropbearNativeError {
@@ -445,6 +446,7 @@ pub enum DropbearNativeError {
     AssetNotFound = -21,
     /// When a handle has been inputted wrongly.
     InvalidHandle = -22,
+    PhysicsObjectNotFound = -23,
     
     /// The entity provided was invalid, likely not from [hecs::Entity::from_bits].
     InvalidEntity = -100,
@@ -490,6 +492,7 @@ impl Display for DropbearNativeError {
             DropbearNativeError::AssetNotFound => "AssetNotFound (-21)",
             DropbearNativeError::InvalidHandle => "InvalidHandle (-22)",
             DropbearNativeError::GenericError => "GenericError (1)",
+            DropbearNativeError::PhysicsObjectNotFound => "PhysicsObjectNotFound (-23)",
         })
     }
 }
\ No newline at end of file
diff --git a/eucalyptus-core/src/transform.rs b/eucalyptus-core/src/transform.rs
index 9743bf1..75b8b52 100644
--- a/eucalyptus-core/src/transform.rs
+++ b/eucalyptus-core/src/transform.rs
@@ -151,7 +151,7 @@ pub mod jni {
     use jni::JNIEnv;
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_EntityTransformNative_entityTransformExistsForEntity(
+    pub extern "system" fn Java_com_dropbear_components_EntityTransformNative_entityTransformExistsForEntity(
         _env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -164,7 +164,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_EntityTransformNative_getLocalTransform(
+    pub extern "system" fn Java_com_dropbear_components_EntityTransformNative_getLocalTransform(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -185,7 +185,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_EntityTransformNative_setLocalTransform(
+    pub extern "system" fn Java_com_dropbear_components_EntityTransformNative_setLocalTransform(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -211,7 +211,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_EntityTransformNative_getWorldTransform(
+    pub extern "system" fn Java_com_dropbear_components_EntityTransformNative_getWorldTransform(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -232,7 +232,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_EntityTransformNative_setWorldTransform(
+    pub extern "system" fn Java_com_dropbear_components_EntityTransformNative_setWorldTransform(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
@@ -255,7 +255,7 @@ pub mod jni {
     }
 
     #[unsafe(no_mangle)]
-    pub fn Java_com_dropbear_components_EntityTransformNative_propagateTransform(
+    pub extern "system" fn Java_com_dropbear_components_EntityTransformNative_propagateTransform(
         mut env: JNIEnv,
         _class: JClass,
         world_ptr: jlong,
diff --git a/eucalyptus-core/src/types.rs b/eucalyptus-core/src/types.rs
index 3c2f319..47e5753 100644
--- a/eucalyptus-core/src/types.rs
+++ b/eucalyptus-core/src/types.rs
@@ -2,6 +2,7 @@
 use glam::{DQuat, DVec3};
 use jni::JNIEnv;
 use jni::objects::{JObject, JValue};
+use jni::sys::jdouble;
 use rapier3d::data::Index;
 use dropbear_engine::entity::Transform;
 use crate::scripting::jni::utils::{FromJObject, ToJObject};
@@ -50,6 +51,59 @@ impl From<Vector3> for glam::DVec3 {
     }
 }
 
+impl FromJObject for Vector3 {
+    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
+    where
+        Self: Sized
+    {
+        let class = env.find_class("com/dropbear/math/Vector3d")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        if !env.is_instance_of(obj, &class)
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?
+        {
+            return Err(DropbearNativeError::InvalidArgument);
+        }
+
+        let x = env.get_field(obj, "x", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let y = env.get_field(obj, "y", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let z = env.get_field(obj, "z", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        Ok(Vector3::new(x, y, z))
+    }
+}
+
+impl ToJObject for Vector3 {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/math/Vector3d")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let constructor_sig = "(DDD)V";
+
+        let args = [
+            jni::objects::JValue::Double(self.x),
+            jni::objects::JValue::Double(self.y),
+            jni::objects::JValue::Double(self.z),
+        ];
+
+        let obj = env.new_object(&class, constructor_sig, &args)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct Quaternion {
@@ -299,4 +353,108 @@ pub struct ColliderShapeFFI {
     pub half_extents_x: f32,
     pub half_extents_y: f32,
     pub half_extents_z: f32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RigidBodyContext {
+    pub index: IndexNative,
+    pub entity_id: u64,
+}
+
+impl FromJObject for RigidBodyContext {
+    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
+    where
+        Self: Sized
+    {
+        let index_obj = env.get_field(obj, "index", "Lcom/dropbear/physics/Index;")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .l()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let idx_val = env.get_field(&index_obj, "index", "I")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .i()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let gen_val = env.get_field(&index_obj, "generation", "I")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .i()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let entity_obj = env.get_field(obj, "entity", "Lcom/dropbear/EntityId;")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .l()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let entity_raw = env.get_field(&entity_obj, "raw", "J")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .j()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        Ok(RigidBodyContext {
+            index: IndexNative {
+                index: idx_val as u32,
+                generation: gen_val as u32,
+            },
+            entity_id: entity_raw as u64,
+        })
+    }
+}
+
+impl ToJObject for RigidBodyContext {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let index_cls = env.find_class("com/dropbear/physics/Index")
+            .map_err(|e| {
+                eprintln!("[JNI Error] Class 'com/dropbear/physics/Index' not found: {:?}", e);
+                DropbearNativeError::JNIClassNotFound
+            })?;
+
+        let entity_cls = env.find_class("com/dropbear/EntityId")
+            .map_err(|e| {
+                eprintln!("[JNI Error] Class 'com/dropbear/EntityId' not found: {:?}", e);
+                DropbearNativeError::JNIClassNotFound
+            })?;
+
+        let rb_cls = env.find_class("com/dropbear/physics/RigidBody")
+            .map_err(|e| {
+                eprintln!("[JNI Error] Class 'com/dropbear/physics/RigidBody' not found: {:?}", e);
+                DropbearNativeError::JNIClassNotFound
+            })?;
+
+        let index_obj = env.new_object(
+            &index_cls,
+            "(II)V",
+            &[
+                JValue::Int(self.index.index as i32),
+                JValue::Int(self.index.generation as i32)
+            ]
+        ).map_err(|e| {
+            eprintln!("[JNI Error] Failed to create Index object: {:?}", e);
+            DropbearNativeError::JNIFailedToCreateObject
+        })?;
+
+        let entity_obj = env.new_object(
+            &entity_cls,
+            "(J)V",
+            &[JValue::Long(self.entity_id as i64)]
+        ).map_err(|e| {
+            eprintln!("[JNI Error] Failed to create EntityId object: {:?}", e);
+            DropbearNativeError::JNIFailedToCreateObject
+        })?;
+
+        let rb_obj = env.new_object(
+            rb_cls,
+            "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;)V",
+            &[
+                JValue::Object(&index_obj),
+                JValue::Object(&entity_obj)
+            ]
+        ).map_err(|e| {
+            eprintln!("[JNI Error] Failed to create RigidBody object: {:?}", e);
+            DropbearNativeError::JNIFailedToCreateObject
+        })?;
+
+        Ok(rb_obj)
+    }
 }
\ No newline at end of file
diff --git a/eucalyptus-editor/README.md b/eucalyptus-editor/README.md
index 5055465..4d2c94f 100644
--- a/eucalyptus-editor/README.md
+++ b/eucalyptus-editor/README.md
@@ -1,3 +1,12 @@
 # eucalyptus-editor
 
-The primary editor used to make games on the dropbear engine.
\ No newline at end of file
+The primary editor used to make games on the dropbear engine. It is scripted in Kotlin and is JVM first-party
+supported. 
+
+## Other functions
+
+To run a local play test, run `eucalyptus-editor.exe play {project_dir}` with optional jvm args specified through
+`--jvm-args {jvm_args}`. 
+
+eucalyptus-editor does not have a scripting interface, therefore can be run without the Java Virtual Machine. If you do
+wish to enter into "Play Mode" or start testing your game, you will require a form of Java Development Kit (JDK).
\ No newline at end of file
diff --git a/eucalyptus-editor/src/physics/rigidbody.rs b/eucalyptus-editor/src/physics/rigidbody.rs
index 62a4f6f..6204385 100644
--- a/eucalyptus-editor/src/physics/rigidbody.rs
+++ b/eucalyptus-editor/src/physics/rigidbody.rs
@@ -41,8 +41,11 @@ impl InspectableComponent for RigidBody {
                     .range(0.0..=10.0));
             });
 
+
             ui.checkbox(&mut self.can_sleep, "Can Sleep");
 
+            ui.checkbox(&mut self.sleeping, "Initially sleeping?");
+
             ui.checkbox(&mut self.ccd_enabled, "CCD Enabled");
 
             ui.add_space(8.0);
diff --git a/headers/dropbear.h b/headers/dropbear.h
deleted file mode 100644
index 90080d8..0000000
--- a/headers/dropbear.h
+++ /dev/null
@@ -1,450 +0,0 @@
-#ifndef DROPBEAR_H
-#define DROPBEAR_H
-
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-typedef enum dropbear_ColliderShapeType {
-    Box = 0,
-    Sphere = 1,
-    Capsule = 2,
-    Cylinder = 3,
-    Cone = 4,
-} dropbear_ColliderShapeType;
-
-typedef struct dropbear_CommandBuffer dropbear_CommandBuffer;
-
-/**
- * Shows the information about the input at that current time.
- */
-typedef struct dropbear_InputState dropbear_InputState;
-
-/**
- * A serializable [rapier3d] state that shows all the different actions and types related
- * to physics rendering.
- */
-typedef struct dropbear_PhysicsState dropbear_PhysicsState;
-
-/**
- * A mutable pointer to a [`World`].
- *
- * Defined in `dropbear_common.h` as `World`
- */
-typedef dropbear_World *dropbear_WorldPtr;
-
-typedef struct dropbear_Vector3 {
-    double x;
-    double y;
-    double z;
-} dropbear_Vector3;
-
-/**
- * A mutable pointer to an [`InputState`].
- *
- * Defined in `dropbear_common.h` as `InputState`
- */
-typedef struct dropbear_InputState *dropbear_InputStatePtr;
-
-typedef struct dropbear_Vector2 {
-    double x;
-    double y;
-} dropbear_Vector2;
-
-/**
- * A non-mutable pointer to a [`crossbeam_channel::Sender`] that sends
- * [`CommandBuffer`] signals.
- *
- * Defined in `dropbear_common.h` as `CommandBuffer`
- */
-typedef const dropbear_Sender<dropbear_CommandBuffer> *dropbear_CommandBufferPtr;
-
-/**
- * A mutable pointer to a [`PhysicsState`].
- *
- * Defined in `dropbear_common.h` as `PhysicsEngine`
- */
-typedef struct dropbear_PhysicsState *dropbear_PhysicsStatePtr;
-
-typedef struct dropbear_IndexNative {
-    uint32_t index;
-    uint32_t generation;
-} dropbear_IndexNative;
-
-typedef struct dropbear_ColliderFFI {
-    struct dropbear_IndexNative index;
-    uint64_t entity_id;
-    uint32_t id;
-} dropbear_ColliderFFI;
-
-typedef struct dropbear_ColliderShapeFFI {
-    enum dropbear_ColliderShapeType shape_type;
-    float radius;
-    float half_height;
-    float half_extents_x;
-    float half_extents_y;
-    float half_extents_z;
-} dropbear_ColliderShapeFFI;
-
-/**
- * A non-mutable pointer to the [`AssetRegistry`].
- *
- * Defined in `dropbear_common.h` as `AssetRegistry`
- */
-typedef const dropbear_AssetRegistry *dropbear_AssetRegistryPtr;
-
-const uint8_t *get_rustc_version(void);
-
-int32_t dropbear_camera_exists_for_entity(dropbear_WorldPtr world_ptr,
-                                          uint64_t entity_id,
-                                          bool *out_result);
-
-int32_t dropbear_get_camera_eye(dropbear_WorldPtr world_ptr,
-                                uint64_t entity_id,
-                                struct dropbear_Vector3 *out_result);
-
-int32_t dropbear_set_camera_eye(dropbear_WorldPtr world_ptr,
-                                uint64_t entity_id,
-                                struct dropbear_Vector3 value);
-
-int32_t dropbear_get_camera_target(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   struct dropbear_Vector3 *out_result);
-
-int32_t dropbear_set_camera_target(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   struct dropbear_Vector3 value);
-
-int32_t dropbear_get_camera_up(dropbear_WorldPtr world_ptr,
-                               uint64_t entity_id,
-                               struct dropbear_Vector3 *out_result);
-
-int32_t dropbear_set_camera_up(dropbear_WorldPtr world_ptr,
-                               uint64_t entity_id,
-                               struct dropbear_Vector3 value);
-
-int32_t dropbear_get_camera_aspect(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   double *out_result);
-
-int32_t dropbear_get_camera_fov_y(dropbear_WorldPtr world_ptr,
-                                  uint64_t entity_id,
-                                  double *out_result);
-
-int32_t dropbear_set_camera_fov_y(dropbear_WorldPtr world_ptr, uint64_t entity_id, double value);
-
-int32_t dropbear_get_camera_znear(dropbear_WorldPtr world_ptr,
-                                  uint64_t entity_id,
-                                  double *out_result);
-
-int32_t dropbear_set_camera_znear(dropbear_WorldPtr world_ptr, uint64_t entity_id, double value);
-
-int32_t dropbear_get_camera_zfar(dropbear_WorldPtr world_ptr,
-                                 uint64_t entity_id,
-                                 double *out_result);
-
-int32_t dropbear_set_camera_zfar(dropbear_WorldPtr world_ptr, uint64_t entity_id, double value);
-
-int32_t dropbear_get_camera_yaw(dropbear_WorldPtr world_ptr,
-                                uint64_t entity_id,
-                                double *out_result);
-
-int32_t dropbear_set_camera_yaw(dropbear_WorldPtr world_ptr, uint64_t entity_id, double value);
-
-int32_t dropbear_get_camera_pitch(dropbear_WorldPtr world_ptr,
-                                  uint64_t entity_id,
-                                  double *out_result);
-
-int32_t dropbear_set_camera_pitch(dropbear_WorldPtr world_ptr, uint64_t entity_id, double value);
-
-int32_t dropbear_get_camera_speed(dropbear_WorldPtr world_ptr,
-                                  uint64_t entity_id,
-                                  double *out_result);
-
-int32_t dropbear_set_camera_speed(dropbear_WorldPtr world_ptr, uint64_t entity_id, double value);
-
-int32_t dropbear_get_camera_sensitivity(dropbear_WorldPtr world_ptr,
-                                        uint64_t entity_id,
-                                        double *out_result);
-
-int32_t dropbear_set_camera_sensitivity(dropbear_WorldPtr world_ptr,
-                                        uint64_t entity_id,
-                                        double value);
-
-int32_t dropbear_is_gamepad_button_pressed(dropbear_InputStatePtr input_ptr,
-                                           uint64_t gamepad_id,
-                                           int32_t button_ordinal,
-                                           bool *out_result);
-
-int32_t dropbear_get_left_stick_position(dropbear_InputStatePtr input_ptr,
-                                         uint64_t gamepad_id,
-                                         struct dropbear_Vector2 *out_result);
-
-int32_t dropbear_get_right_stick_position(dropbear_InputStatePtr input_ptr,
-                                          uint64_t gamepad_id,
-                                          struct dropbear_Vector2 *out_result);
-
-int32_t dropbear_free_gamepads_array(uint64_t *ptr, uintptr_t count);
-
-int32_t dropbear_print_input_state(dropbear_InputStatePtr input_ptr);
-
-int32_t dropbear_is_key_pressed(dropbear_InputStatePtr input_ptr,
-                                int32_t key_ordinal,
-                                bool *out_result);
-
-int32_t dropbear_get_mouse_position(dropbear_InputStatePtr input_ptr,
-                                    struct dropbear_Vector2 *out_result);
-
-int32_t dropbear_is_mouse_button_pressed(dropbear_InputStatePtr input_ptr,
-                                         int32_t btn_ordinal,
-                                         bool *out_result);
-
-int32_t dropbear_get_mouse_delta(dropbear_InputStatePtr input_ptr,
-                                 struct dropbear_Vector2 *out_result);
-
-int32_t dropbear_is_cursor_locked(dropbear_InputStatePtr input_ptr, bool *out_result);
-
-int32_t dropbear_set_cursor_locked(dropbear_CommandBufferPtr cmd_ptr,
-                                   dropbear_InputStatePtr input_ptr,
-                                   bool locked);
-
-int32_t dropbear_get_last_mouse_pos(dropbear_InputStatePtr input_ptr,
-                                    struct dropbear_Vector2 *out_result);
-
-int32_t dropbear_is_cursor_hidden(dropbear_InputStatePtr input_ptr, bool *out_result);
-
-int32_t dropbear_set_cursor_hidden(dropbear_CommandBufferPtr cmd_ptr,
-                                   dropbear_InputStatePtr input_ptr,
-                                   bool hidden);
-
-int32_t dropbear_get_connected_gamepads(dropbear_InputStatePtr input_ptr,
-                                        uintptr_t *out_count,
-                                        uint64_t **out_result);
-
-int32_t dropbear_free_string(char *ptr);
-
-int32_t dropbear_collider_group_exists_for_entity(dropbear_WorldPtr world_ptr,
-                                                  uint64_t entity_id,
-                                                  bool *out_result);
-
-int32_t dropbear_get_collider_group_colliders(dropbear_WorldPtr world_ptr,
-                                              dropbear_PhysicsStatePtr physics_ptr,
-                                              uint64_t entity_id,
-                                              uintptr_t *out_count,
-                                              struct dropbear_ColliderFFI **out_result);
-
-int32_t dropbear_free_collider_array(struct dropbear_ColliderFFI *ptr, uintptr_t count);
-
-int32_t dropbear_get_collider_shape(dropbear_PhysicsStatePtr physics_ptr,
-                                    struct dropbear_ColliderFFI ffi,
-                                    struct dropbear_ColliderShapeFFI *out_result);
-
-int32_t dropbear_set_collider_shape(dropbear_PhysicsStatePtr physics_ptr,
-                                    struct dropbear_ColliderFFI ffi,
-                                    struct dropbear_ColliderShapeFFI shape);
-
-int32_t dropbear_get_collider_density(dropbear_PhysicsStatePtr physics_ptr,
-                                      struct dropbear_ColliderFFI ffi,
-                                      double *out_result);
-
-int32_t dropbear_set_collider_density(dropbear_PhysicsStatePtr physics_ptr,
-                                      struct dropbear_ColliderFFI ffi,
-                                      double density);
-
-int32_t dropbear_get_collider_friction(dropbear_PhysicsStatePtr physics_ptr,
-                                       struct dropbear_ColliderFFI ffi,
-                                       double *out_result);
-
-int32_t dropbear_set_collider_friction(dropbear_PhysicsStatePtr physics_ptr,
-                                       struct dropbear_ColliderFFI ffi,
-                                       double friction);
-
-int32_t dropbear_get_collider_restitution(dropbear_PhysicsStatePtr physics_ptr,
-                                          struct dropbear_ColliderFFI ffi,
-                                          double *out_result);
-
-int32_t dropbear_set_collider_restitution(dropbear_PhysicsStatePtr physics_ptr,
-                                          struct dropbear_ColliderFFI ffi,
-                                          double restitution);
-
-int32_t dropbear_get_collider_mass(dropbear_PhysicsStatePtr physics_ptr,
-                                   struct dropbear_ColliderFFI ffi,
-                                   double *out_result);
-
-int32_t dropbear_set_collider_mass(dropbear_PhysicsStatePtr physics_ptr,
-                                   struct dropbear_ColliderFFI ffi,
-                                   double mass);
-
-int32_t dropbear_get_collider_is_sensor(dropbear_PhysicsStatePtr physics_ptr,
-                                        struct dropbear_ColliderFFI ffi,
-                                        bool *out_result);
-
-int32_t dropbear_set_collider_is_sensor(dropbear_PhysicsStatePtr physics_ptr,
-                                        struct dropbear_ColliderFFI ffi,
-                                        bool is_sensor);
-
-int32_t dropbear_get_collider_translation(dropbear_PhysicsStatePtr physics_ptr,
-                                          struct dropbear_ColliderFFI ffi,
-                                          struct dropbear_Vector3 *out_result);
-
-int32_t dropbear_set_collider_translation(dropbear_PhysicsStatePtr physics_ptr,
-                                          struct dropbear_ColliderFFI ffi,
-                                          struct dropbear_Vector3 translation);
-
-int32_t dropbear_get_collider_rotation(dropbear_PhysicsStatePtr physics_ptr,
-                                       struct dropbear_ColliderFFI ffi,
-                                       struct dropbear_Vector3 *out_result);
-
-int32_t dropbear_set_collider_rotation(dropbear_PhysicsStatePtr physics_ptr,
-                                       struct dropbear_ColliderFFI ffi,
-                                       struct dropbear_Vector3 rotation);
-
-int32_t dropbear_get_texture_name(dropbear_AssetRegistryPtr asset_registry_ptr,
-                                  uint64_t handle,
-                                  char **out_result);
-
-int32_t dropbear_is_model_handle(dropbear_AssetRegistryPtr asset_registry_ptr,
-                                 uint64_t handle,
-                                 bool *out_result);
-
-int32_t dropbear_is_texture_handle(dropbear_AssetRegistryPtr asset_registry_ptr,
-                                   uint64_t handle,
-                                   bool *out_result);
-
-int32_t dropbear_custom_properties_exists_for_entity(dropbear_WorldPtr world_ptr,
-                                                     uint64_t entity_id,
-                                                     bool *out_result);
-
-int32_t dropbear_get_string_property(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     const char *key,
-                                     char **out_result);
-
-int32_t dropbear_get_int_property(dropbear_WorldPtr world_ptr,
-                                  uint64_t entity_id,
-                                  const char *key,
-                                  int32_t *out_result);
-
-int32_t dropbear_get_long_property(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   const char *key,
-                                   int64_t *out_result);
-
-int32_t dropbear_get_double_property(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     const char *key,
-                                     double *out_result);
-
-int32_t dropbear_get_float_property(dropbear_WorldPtr world_ptr,
-                                    uint64_t entity_id,
-                                    const char *key,
-                                    float *out_result);
-
-int32_t dropbear_get_bool_property(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   const char *key,
-                                   bool *out_result);
-
-int32_t dropbear_get_vec3_property(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   const char *key,
-                                   struct dropbear_Vector3 *out_result);
-
-int32_t dropbear_set_string_property(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     const char *key,
-                                     const char *value);
-
-int32_t dropbear_set_int_property(dropbear_WorldPtr world_ptr,
-                                  uint64_t entity_id,
-                                  const char *key,
-                                  int32_t value);
-
-int32_t dropbear_set_long_property(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   const char *key,
-                                   int64_t value);
-
-int32_t dropbear_set_double_property(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     const char *key,
-                                     double value);
-
-int32_t dropbear_set_float_property(dropbear_WorldPtr world_ptr,
-                                    uint64_t entity_id,
-                                    const char *key,
-                                    float value);
-
-int32_t dropbear_set_bool_property(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   const char *key,
-                                   bool value);
-
-int32_t dropbear_set_vec3_property(dropbear_WorldPtr world_ptr,
-                                   uint64_t entity_id,
-                                   const char *key,
-                                   struct dropbear_Vector3 value);
-
-int32_t dropbear_mesh_renderer_exists_for_entity(dropbear_WorldPtr world_ptr,
-                                                 uint64_t entity_id,
-                                                 bool *out_result);
-
-int32_t dropbear_get_model(dropbear_WorldPtr world_ptr, uint64_t entity_id, uint64_t *out_result);
-
-int32_t dropbear_set_model(dropbear_WorldPtr world_ptr,
-                           dropbear_AssetRegistryPtr asset_ptr,
-                           uint64_t entity_id,
-                           uint64_t model_id);
-
-int32_t dropbear_get_all_texture_ids(dropbear_WorldPtr world_ptr,
-                                     dropbear_AssetRegistryPtr asset_ptr,
-                                     uint64_t entity_id,
-                                     uintptr_t *out_count,
-                                     uint64_t **out_result);
-
-int32_t dropbear_get_texture(dropbear_WorldPtr world_ptr,
-                             dropbear_AssetRegistryPtr asset_ptr,
-                             uint64_t entity_id,
-                             const char *material_name,
-                             uint64_t *out_result);
-
-int32_t dropbear_set_texture_override(dropbear_WorldPtr world_ptr,
-                                      dropbear_AssetRegistryPtr asset_ptr,
-                                      uint64_t entity_id,
-                                      const char *material_name,
-                                      uint64_t texture_handle);
-
-int32_t dropbear_get_entity(dropbear_WorldPtr world_ptr, const char *label, uint64_t *out_result);
-
-int32_t dropbear_get_asset(dropbear_AssetRegistryPtr asset_ptr,
-                           const char *uri,
-                           uint64_t *out_result);
-
-int32_t dropbear_quit(dropbear_CommandBufferPtr command_ptr);
-
-int32_t dropbear_entity_transform_exists_for_entity(dropbear_WorldPtr world_ptr,
-                                                    uint64_t entity_id,
-                                                    bool *out_result);
-
-int32_t dropbear_get_local_transform(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     dropbear_Transform *out_result);
-
-int32_t dropbear_set_local_transform(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     dropbear_Transform value);
-
-int32_t dropbear_get_world_transform(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     dropbear_Transform *out_result);
-
-int32_t dropbear_set_world_transform(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     dropbear_Transform value);
-
-int32_t dropbear_propagate_transform(dropbear_WorldPtr world_ptr,
-                                     uint64_t entity_id,
-                                     dropbear_Transform *out_result);
-
-#endif  /* DROPBEAR_H */
diff --git a/include/dropbear.h b/include/dropbear.h
new file mode 100644
index 0000000..ff9c9aa
--- /dev/null
+++ b/include/dropbear.h
@@ -0,0 +1,19 @@
+#pragma once
+
+typedef struct World World; // opaque pointer
+typedef struct InputState InputState; // opaque pointer
+typedef struct CommandBuffer CommandBuffer; // opaque pointer
+typedef struct AssetRegistry AssetRegistry; // opaque pointer
+typedef struct SceneLoader SceneLoader; // opaque pointer
+typedef struct PhysicsEngine PhysicsEngine; // opaque pointer
+
+/// Describes all the different pointers that can be passed into a scripting
+/// module.
+typedef struct {
+    World* world;
+    InputState* input;
+    CommandBuffer* graphics;
+    AssetRegistry* assets;
+    SceneLoader* scene_loader;
+    PhysicsEngine* physics_engine;
+} DropbearContext;
diff --git a/magna-carta/src/generator/jvm.rs b/magna-carta/src/generator/jvm.rs
index 1c1fd79..bc87cf9 100644
--- a/magna-carta/src/generator/jvm.rs
+++ b/magna-carta/src/generator/jvm.rs
@@ -52,7 +52,7 @@ impl Generator for KotlinJVMGenerator {
         writeln!(output, "object RunnableRegistry {{")?;
         writeln!(
             output,
-            "    private val tagRegistry = mutableMapOf<String, MutableList<() -> com.dropbear.System>>()"
+            "    private val tagRegistry = mutableMapOf<String, MutableList<() -> com.dropbear.ecs.System>>()"
         )?;
         writeln!(output)?;
 
@@ -84,7 +84,7 @@ impl Generator for KotlinJVMGenerator {
 
         writeln!(
             output,
-            "    fun getScriptFactories(tag: String): List<() -> com.dropbear.System> {{"
+            "    fun getScriptFactories(tag: String): List<() -> com.dropbear.ecs.System> {{"
         )?;
         writeln!(
             output,
@@ -95,7 +95,7 @@ impl Generator for KotlinJVMGenerator {
 
         writeln!(
             output,
-            "    fun instantiateScripts(tag: String): List<com.dropbear.System> {{"
+            "    fun instantiateScripts(tag: String): List<com.dropbear.ecs.System> {{"
         )?;
         writeln!(
             output,
diff --git a/magna-carta/src/generator/native.rs b/magna-carta/src/generator/native.rs
index 67f44e4..54d0203 100644
--- a/magna-carta/src/generator/native.rs
+++ b/magna-carta/src/generator/native.rs
@@ -28,7 +28,7 @@ impl Generator for KotlinNativeGenerator {
         writeln!(
             output,
             r#"import com.dropbear.DropbearEngine
-import com.dropbear.System
+import com.dropbear.ecs.System
 import com.dropbear.ffi.NativeEngine
 import com.dropbear.ffi.generated.DropbearContext
 import com.dropbear.logging.Logger
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 8d0a1ea..d353976 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -16,5 +16,4 @@ buildCache {
     local {
         isEnabled = true
     }
-}
-//include("dropbear-gradle-plugin")
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/README.md b/src/README.md
index e69de29..17179a8 100644
--- a/src/README.md
+++ b/src/README.md
@@ -0,0 +1,4 @@
+# dropbear
+
+This is the scripting module API interface used for adding logic to entities and scenes on eucalyptus-editor based
+games.
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
index 30c8dce..141bab0 100644
--- a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
@@ -17,22 +17,8 @@ var lastErrorMessage: String? = null
  * which contains a [NativeEngine] (contains native functions). 
  */
 class DropbearEngine(val native: NativeEngine) {
-    private var inputState: InputState? = null
-        get() {
-            if (field == null) {
-                Logger.trace("InputState not initialised, creating new one")
-                field = InputState()
-            }
-            return field
-        }
-    private var sceneManager: SceneManager? = null
-        get() {
-            if (field == null) {
-                Logger.trace("SceneManager not initialised, creating new one")
-                field = SceneManager()
-            }
-            return field
-        }
+    val inputState: InputState = InputState()
+    val sceneManager: SceneManager = SceneManager()
 
     init {
         Companion.native = native
diff --git a/src/commonMain/kotlin/com/dropbear/EntityRef.kt b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
index 6e6e3e9..0d6e7b7 100644
--- a/src/commonMain/kotlin/com/dropbear/EntityRef.kt
+++ b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
@@ -29,7 +29,7 @@ class EntityRef(val id: EntityId = EntityId(0L)) {
         return "EntityRef(id=$id)"
     }
 
-    fun <T : Component> get(type: ComponentType<T>): T? {
+    fun <T : Component> getComponent(type: ComponentType<T>): T? {
         return type.get(id)
     }
 
diff --git a/src/commonMain/kotlin/com/dropbear/physics/RigidBody.kt b/src/commonMain/kotlin/com/dropbear/physics/RigidBody.kt
index 2662fed..6ee7fdd 100644
--- a/src/commonMain/kotlin/com/dropbear/physics/RigidBody.kt
+++ b/src/commonMain/kotlin/com/dropbear/physics/RigidBody.kt
@@ -16,8 +16,8 @@ class RigidBody(
         get() = getRigidbodyGravityScale(this)
         set(value) = setRigidbodyGravityScale(this, value)
     var canSleep: Boolean
-        get() = getRigidbodyCanSleep(this)
-        set(value) = setRigidbodyCanSleep(this, value)
+        get() = getRigidBodySleep(this)
+        set(value) = setRigidBodySleep(this, value)
     var ccdEnabled: Boolean
         get() = getRigidbodyCcdEnabled(this)
         set(value) = setRigidbodyCcdEnabled(this, value)
@@ -39,9 +39,8 @@ class RigidBody(
     var lockRotation: AxisLock
         get() = getRigidbodyLockRotation(this)
         set(value) = setRigidbodyLockRotation(this, value)
-    var childColliders: List<EntityId>
+    val childColliders: List<Collider>
         get() = getRigidbodyChildren(this)
-        set(value) = setRigidbodyChildren(this, value)
 
     /**
      * Applies an instant force.
@@ -91,8 +90,8 @@ expect fun RigidBody.getRigidbodyMode(rigidBody: RigidBody): RigidBodyMode
 expect fun RigidBody.setRigidbodyMode(rigidBody: RigidBody, mode: RigidBodyMode)
 expect fun RigidBody.getRigidbodyGravityScale(rigidBody: RigidBody): Double
 expect fun RigidBody.setRigidbodyGravityScale(rigidBody: RigidBody, gravityScale: Double)
-expect fun RigidBody.getRigidbodyCanSleep(rigidBody: RigidBody): Boolean
-expect fun RigidBody.setRigidbodyCanSleep(rigidBody: RigidBody, canSleep: Boolean)
+expect fun RigidBody.getRigidBodySleep(rigidBody: RigidBody): Boolean
+expect fun RigidBody.setRigidBodySleep(rigidBody: RigidBody, canSleep: Boolean)
 expect fun RigidBody.getRigidbodyCcdEnabled(rigidBody: RigidBody): Boolean
 expect fun RigidBody.setRigidbodyCcdEnabled(rigidBody: RigidBody, ccdEnabled: Boolean)
 expect fun RigidBody.getRigidbodyLinearVelocity(rigidBody: RigidBody): Vector3d
@@ -107,8 +106,7 @@ expect fun RigidBody.getRigidbodyLockTranslation(rigidBody: RigidBody): AxisLock
 expect fun RigidBody.setRigidbodyLockTranslation(rigidBody: RigidBody, lockTranslation: AxisLock)
 expect fun RigidBody.getRigidbodyLockRotation(rigidBody: RigidBody): AxisLock
 expect fun RigidBody.setRigidbodyLockRotation(rigidBody: RigidBody, lockRotation: AxisLock)
-expect fun RigidBody.getRigidbodyChildren(rigidBody: RigidBody): List<EntityId>
-expect fun RigidBody.setRigidbodyChildren(rigidBody: RigidBody, children: List<EntityId>)
+expect fun RigidBody.getRigidbodyChildren(rigidBody: RigidBody): List<Collider>
 expect fun RigidBody.applyImpulse(index: Index, x: Double, y: Double, z: Double)
 expect fun RigidBody.applyTorqueImpulse(index: Index, x: Double, y: Double, z: Double)
 
diff --git a/src/dropbear.def b/src/dropbear.def
index 8d1d389..1c12c33 100644
--- a/src/dropbear.def
+++ b/src/dropbear.def
@@ -1,4 +1,4 @@
 package: com.dropbear.ffi.generated
 headers: dropbear.h
-headerFilter: *.h components/*.h
+headerFilter: *.h
 libraryPaths: src
\ No newline at end of file
diff --git a/src/jvmMain/kotlin/com/dropbear/physics/RigidBody.jvm.kt b/src/jvmMain/kotlin/com/dropbear/physics/RigidBody.jvm.kt
index a1b3b27..caa7f73 100644
--- a/src/jvmMain/kotlin/com/dropbear/physics/RigidBody.jvm.kt
+++ b/src/jvmMain/kotlin/com/dropbear/physics/RigidBody.jvm.kt
@@ -5,7 +5,7 @@ import com.dropbear.EntityId
 import com.dropbear.math.Vector3d
 
 actual fun RigidBody.getRigidbodyMode(rigidBody: RigidBody): RigidBodyMode {
-    val result = RigidBodyNative.getRigidBodyMode(DropbearEngine.native.physicsEngineHandle, this)
+    val result = RigidBodyNative.getRigidBodyMode(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
     return RigidBodyMode.entries[result]
 }
 
@@ -13,44 +13,44 @@ actual fun RigidBody.setRigidbodyMode(
     rigidBody: RigidBody,
     mode: RigidBodyMode
 ) {
-    RigidBodyNative.setRigidBodyMode(DropbearEngine.native.physicsEngineHandle, this, mode.ordinal)
+    RigidBodyNative.setRigidBodyMode(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, mode.ordinal)
 }
 
 actual fun RigidBody.getRigidbodyGravityScale(rigidBody: RigidBody): Double {
-    return RigidBodyNative.getRigidBodyGravityScale(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyGravityScale(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
 actual fun RigidBody.setRigidbodyGravityScale(
     rigidBody: RigidBody,
     gravityScale: Double
 ) {
-    RigidBodyNative.setRigidBodyGravityScale(DropbearEngine.native.physicsEngineHandle, this, gravityScale)
+    RigidBodyNative.setRigidBodyGravityScale(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, gravityScale)
 }
 
-actual fun RigidBody.getRigidbodyCanSleep(rigidBody: RigidBody): Boolean {
-    return RigidBodyNative.getRigidBodyCanSleep(DropbearEngine.native.physicsEngineHandle, this)
+actual fun RigidBody.getRigidBodySleep(rigidBody: RigidBody): Boolean {
+    return RigidBodyNative.getRigidBodySleep(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
-actual fun RigidBody.setRigidbodyCanSleep(
+actual fun RigidBody.setRigidBodySleep(
     rigidBody: RigidBody,
     canSleep: Boolean
 ) {
-    RigidBodyNative.setRigidBodyCanSleep(DropbearEngine.native.physicsEngineHandle, this, canSleep)
+    RigidBodyNative.setRigidBodySleep(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, canSleep)
 }
 
 actual fun RigidBody.getRigidbodyCcdEnabled(rigidBody: RigidBody): Boolean {
-    return RigidBodyNative.getRigidBodyCcdEnabled(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyCcdEnabled(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
 actual fun RigidBody.setRigidbodyCcdEnabled(
     rigidBody: RigidBody,
     ccdEnabled: Boolean
 ) {
-    RigidBodyNative.setRigidBodyCcdEnabled(DropbearEngine.native.physicsEngineHandle, this, ccdEnabled)
+    RigidBodyNative.setRigidBodyCcdEnabled(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, ccdEnabled)
 }
 
 actual fun RigidBody.getRigidbodyLinearVelocity(rigidBody: RigidBody): Vector3d {
-    return RigidBodyNative.getRigidBodyLinearVelocity(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyLinearVelocity(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
         ?: Vector3d.zero()
 }
 
@@ -58,11 +58,11 @@ actual fun RigidBody.setRigidbodyLinearVelocity(
     rigidBody: RigidBody,
     linearVelocity: Vector3d
 ) {
-    RigidBodyNative.setRigidBodyLinearVelocity(DropbearEngine.native.physicsEngineHandle, this, linearVelocity)
+    RigidBodyNative.setRigidBodyLinearVelocity(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, linearVelocity)
 }
 
 actual fun RigidBody.getRigidbodyAngularVelocity(rigidBody: RigidBody): Vector3d {
-    return RigidBodyNative.getRigidBodyAngularVelocity(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyAngularVelocity(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
         ?: Vector3d.zero()
 }
 
@@ -70,64 +70,56 @@ actual fun RigidBody.setRigidbodyAngularVelocity(
     rigidBody: RigidBody,
     angularVelocity: Vector3d
 ) {
-    RigidBodyNative.setRigidBodyAngularVelocity(DropbearEngine.native.physicsEngineHandle, this, angularVelocity)
+    RigidBodyNative.setRigidBodyAngularVelocity(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, angularVelocity)
 }
 
 actual fun RigidBody.getRigidbodyLinearDamping(rigidBody: RigidBody): Double {
-    return RigidBodyNative.getRigidBodyLinearDamping(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyLinearDamping(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
 actual fun RigidBody.setRigidbodyLinearDamping(
     rigidBody: RigidBody,
     linearDamping: Double
 ) {
-    RigidBodyNative.setRigidBodyLinearDamping(DropbearEngine.native.physicsEngineHandle, this, linearDamping)
+    RigidBodyNative.setRigidBodyLinearDamping(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, linearDamping)
 }
 
 actual fun RigidBody.getRigidbodyAngularDamping(rigidBody: RigidBody): Double {
-    return RigidBodyNative.getRigidBodyAngularDamping(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyAngularDamping(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
 actual fun RigidBody.setRigidbodyAngularDamping(
     rigidBody: RigidBody,
     angularDamping: Double
 ) {
-    RigidBodyNative.setRigidBodyAngularDamping(DropbearEngine.native.physicsEngineHandle, this, angularDamping)
+    RigidBodyNative.setRigidBodyAngularDamping(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, angularDamping)
 }
 
 actual fun RigidBody.getRigidbodyLockTranslation(rigidBody: RigidBody): AxisLock {
-    return RigidBodyNative.getRigidBodyLockTranslation(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyLockTranslation(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
 actual fun RigidBody.setRigidbodyLockTranslation(
     rigidBody: RigidBody,
     lockTranslation: AxisLock
 ) {
-    RigidBodyNative.setRigidBodyLockTranslation(DropbearEngine.native.physicsEngineHandle, this, lockTranslation)
+    RigidBodyNative.setRigidBodyLockTranslation(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, lockTranslation)
 }
 
 actual fun RigidBody.getRigidbodyLockRotation(rigidBody: RigidBody): AxisLock {
-    return RigidBodyNative.getRigidBodyLockRotation(DropbearEngine.native.physicsEngineHandle, this)
+    return RigidBodyNative.getRigidBodyLockRotation(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
 }
 
 actual fun RigidBody.setRigidbodyLockRotation(
     rigidBody: RigidBody,
     lockRotation: AxisLock
 ) {
-    RigidBodyNative.setRigidBodyLockRotation(DropbearEngine.native.physicsEngineHandle, this, lockRotation)
+    RigidBodyNative.setRigidBodyLockRotation(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, lockRotation)
 }
 
-actual fun RigidBody.getRigidbodyChildren(rigidBody: RigidBody): List<EntityId> {
-    val result = RigidBodyNative.getRigidBodyChildren(DropbearEngine.native.physicsEngineHandle, this)
-    return result.map { EntityId(it) }
-}
-
-actual fun RigidBody.setRigidbodyChildren(
-    rigidBody: RigidBody,
-    children: List<EntityId>
-) {
-    val ids = children.map { it.raw }.toLongArray()
-    RigidBodyNative.setRigidBodyChildren(DropbearEngine.native.physicsEngineHandle, this, ids)
+actual fun RigidBody.getRigidbodyChildren(rigidBody: RigidBody): List<Collider> {
+    val result = RigidBodyNative.getRigidBodyChildren(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this)
+    return result.toList()
 }
 
 actual fun RigidBody.applyImpulse(
@@ -136,7 +128,7 @@ actual fun RigidBody.applyImpulse(
     y: Double,
     z: Double
 ) {
-    RigidBodyNative.applyImpulse(DropbearEngine.native.physicsEngineHandle, this, x, y, z)
+    RigidBodyNative.applyImpulse(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, x, y, z)
 }
 
 actual fun RigidBody.applyTorqueImpulse(
@@ -145,9 +137,13 @@ actual fun RigidBody.applyTorqueImpulse(
     y: Double,
     z: Double
 ) {
-    RigidBodyNative.applyTorqueImpulse(DropbearEngine.native.physicsEngineHandle, this, x, y, z)
+    RigidBodyNative.applyTorqueImpulse(DropbearEngine.native.worldHandle, DropbearEngine.native.physicsEngineHandle, this, x, y, z)
 }
 
 actual fun rigidBodyExistsForEntity(entityId: EntityId): Index? {
-    return RigidBodyNative.rigidBodyExistsForEntity(DropbearEngine.native.physicsEngineHandle, entityId.raw)
+    return RigidBodyNative.rigidBodyExistsForEntity(
+        DropbearEngine.native.worldHandle,
+        DropbearEngine.native.physicsEngineHandle,
+        entityId.raw
+    )
 }
\ No newline at end of file
diff --git a/src/jvmMain/kotlin/com/dropbear/physics/RigidBodyNative.java b/src/jvmMain/kotlin/com/dropbear/physics/RigidBodyNative.java
index 5b90ba6..cf3fa23 100644
--- a/src/jvmMain/kotlin/com/dropbear/physics/RigidBodyNative.java
+++ b/src/jvmMain/kotlin/com/dropbear/physics/RigidBodyNative.java
@@ -8,41 +8,40 @@ public class RigidBodyNative {
         new EucalyptusCoreLoader().ensureLoaded();
     }
 
-    public static native Index rigidBodyExistsForEntity(long physicsPtr, long entityId);
+    public static native Index rigidBodyExistsForEntity(long worldPtr, long physicsPtr, long entityId);
 
-    public static native int getRigidBodyMode(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyMode(long physicsPtr, RigidBody rigidBody, int mode);
+    public static native int getRigidBodyMode(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyMode(long worldPtr, long physicsPtr, RigidBody rigidBody, int mode);
 
-    public static native double getRigidBodyGravityScale(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyGravityScale(long physicsPtr, RigidBody rigidBody, double gravityScale);
+    public static native double getRigidBodyGravityScale(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyGravityScale(long worldPtr, long physicsPtr, RigidBody rigidBody, double gravityScale);
 
-    public static native double getRigidBodyLinearDamping(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyLinearDamping(long physicsPtr, RigidBody rigidBody, double linearDamping);
+    public static native double getRigidBodyLinearDamping(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyLinearDamping(long worldPtr, long physicsPtr, RigidBody rigidBody, double linearDamping);
 
-    public static native double getRigidBodyAngularDamping(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyAngularDamping(long physicsPtr, RigidBody rigidBody, double angularDamping);
+    public static native double getRigidBodyAngularDamping(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyAngularDamping(long worldPtr, long physicsPtr, RigidBody rigidBody, double angularDamping);
 
-    public static native boolean getRigidBodyCanSleep(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyCanSleep(long physicsPtr, RigidBody rigidBody, boolean canSleep);
+    public static native boolean getRigidBodySleep(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodySleep(long worldPtr, long physicsPtr, RigidBody rigidBody, boolean canSleep);
 
-    public static native boolean getRigidBodyCcdEnabled(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyCcdEnabled(long physicsPtr, RigidBody rigidBody, boolean ccdEnabled);
+    public static native boolean getRigidBodyCcdEnabled(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyCcdEnabled(long worldPtr, long physicsPtr, RigidBody rigidBody, boolean ccdEnabled);
 
-    public static native Vector3d getRigidBodyLinearVelocity(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyLinearVelocity(long physicsPtr, RigidBody rigidBody, Vector3d linearVelocity);
+    public static native Vector3d getRigidBodyLinearVelocity(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyLinearVelocity(long worldPtr, long physicsPtr, RigidBody rigidBody, Vector3d linearVelocity);
 
-    public static native Vector3d getRigidBodyAngularVelocity(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyAngularVelocity(long physicsPtr, RigidBody rigidBody, Vector3d angularVelocity);
+    public static native Vector3d getRigidBodyAngularVelocity(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyAngularVelocity(long worldPtr, long physicsPtr, RigidBody rigidBody, Vector3d angularVelocity);
 
-    public static native AxisLock getRigidBodyLockTranslation(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyLockTranslation(long physicsPtr, RigidBody rigidBody, AxisLock lockTranslation);
+    public static native AxisLock getRigidBodyLockTranslation(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyLockTranslation(long worldPtr, long physicsPtr, RigidBody rigidBody, AxisLock lockTranslation);
 
-    public static native AxisLock getRigidBodyLockRotation(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyLockRotation(long physicsPtr, RigidBody rigidBody, AxisLock lockRotation);
+    public static native AxisLock getRigidBodyLockRotation(long worldPtr, long physicsPtr, RigidBody rigidBody);
+    public static native void setRigidBodyLockRotation(long worldPtr, long physicsPtr, RigidBody rigidBody, AxisLock lockRotation);
 
-    public static native long[] getRigidBodyChildren(long physicsPtr, RigidBody rigidBody);
-    public static native void setRigidBodyChildren(long physicsPtr, RigidBody rigidBody, long[] children);
+    public static native Collider[] getRigidBodyChildren(long worldPtr, long physicsPtr, RigidBody rigidBody);
 
-    public static native void applyImpulse(long physicsPtr, RigidBody rigidBody, double x, double y, double z);
-    public static native void applyTorqueImpulse(long physicsPtr, RigidBody rigidBody, double x, double y, double z);
+    public static native void applyImpulse(long worldPtr, long physicsPtr, RigidBody rigidBody, double x, double y, double z);
+    public static native void applyTorqueImpulse(long worldPtr, long physicsPtr, RigidBody rigidBody, double x, double y, double z);
 }
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/com/dropbear/physics/RigidBody.native.kt b/src/nativeMain/kotlin/com/dropbear/physics/RigidBody.native.kt
index 8ae6c95..d45559f 100644
--- a/src/nativeMain/kotlin/com/dropbear/physics/RigidBody.native.kt
+++ b/src/nativeMain/kotlin/com/dropbear/physics/RigidBody.native.kt
@@ -23,11 +23,11 @@ actual fun RigidBody.setRigidbodyGravityScale(
 ) {
 }
 
-actual fun RigidBody.getRigidbodyCanSleep(rigidBody: RigidBody): Boolean {
+actual fun RigidBody.getRigidBodySleep(rigidBody: RigidBody): Boolean {
     TODO("Not yet implemented")
 }
 
-actual fun RigidBody.setRigidbodyCanSleep(
+actual fun RigidBody.setRigidBodySleep(
     rigidBody: RigidBody,
     canSleep: Boolean
 ) {
@@ -103,16 +103,10 @@ actual fun RigidBody.setRigidbodyLockRotation(
 ) {
 }
 
-actual fun RigidBody.getRigidbodyChildren(rigidBody: RigidBody): List<EntityId> {
+actual fun RigidBody.getRigidbodyChildren(rigidBody: RigidBody): List<Collider> {
     TODO("Not yet implemented")
 }
 
-actual fun RigidBody.setRigidbodyChildren(
-    rigidBody: RigidBody,
-    children: List<EntityId>
-) {
-}
-
 actual fun RigidBody.applyImpulse(
     index: Index,
     x: Double,
diff --git a/src/quick.py b/src/quick.py
deleted file mode 100644
index 78c8ac2..0000000
--- a/src/quick.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python3
-import subprocess
-import sys
-from pathlib import Path
-
-def main():
-    # Get the script directory and workspace root
-    script_dir = Path(__file__).parent.resolve()
-    root_dir = script_dir.parent if script_dir.name == "scripts" else script_dir
-
-    print("Expanding macros for eucalyptus-core...")
-
-    # Run cargo expand
-    result = subprocess.run(
-        ["cargo", "expand", "--lib", "-p", "eucalyptus-core"],
-        cwd=root_dir,
-        capture_output=True,
-        text=True
-    )
-
-    if result.returncode != 0:
-        print(f"Error: cargo expand failed", file=sys.stderr)
-        print(result.stderr, file=sys.stderr)
-        sys.exit(1)
-
-    # The expanded code is in stdout
-    expanded_code = result.stdout
-
-    # Write expanded code to temp file
-    temp_dir = root_dir / "target" / "generated"
-    temp_dir.mkdir(parents=True, exist_ok=True)
-    temp_file = temp_dir / "expanded.rs"
-
-    print(f"Writing expanded code to {temp_file}")
-    temp_file.write_text(expanded_code, encoding="utf-8")
-
-    # Run cbindgen
-    print("Generating C bindings...")
-    output_file = root_dir / "headers" / "dropbear.h"
-    output_file.parent.mkdir(parents=True, exist_ok=True)
-
-    cbindgen_config = root_dir / "cbindgen.toml"
-
-    result = subprocess.run(
-        [
-            "cbindgen",
-            "--config", str(cbindgen_config),
-            "--output", str(output_file),
-            str(temp_file)
-        ],
-        cwd=root_dir,
-        capture_output=True,
-        text=True
-    )
-
-    if result.returncode != 0:
-        print(f"Error: cbindgen failed", file=sys.stderr)
-        print(result.stderr, file=sys.stderr)
-        sys.exit(1)
-
-    print(f"✓ Generated {output_file}")
-
-if __name__ == "__main__":
-    main()
\ No newline at end of file