tirbofish/dropbear · commit
f5ccc4013bd5cc8fc8aadc96b5ae2d35866d2f7c
wip: implemented JNI function headers (finally). doing some testing.
Signature present but could not be verified.
Unverified
@@ -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 {} } @@ -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") - } - } -} @@ -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") - } -} @@ -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. +[//]: # () +[//]: # (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, `) + +[//]: # ( - ) @@ -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")] @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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)) @@ -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, @@ -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, @@ -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] @@ -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, @@ -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) + } } @@ -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; @@ -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." @@ -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)", }) } } @@ -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, @@ -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) + } } @@ -1,3 +1,12 @@ # eucalyptus-editor -The primary editor used to make games on the dropbear engine. +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). @@ -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); @@ -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 */ @@ -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; @@ -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, @@ -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 @@ -16,5 +16,4 @@ buildCache { local { isEnabled = true } -} -//include("dropbear-gradle-plugin") +} @@ -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. @@ -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 @@ -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) } @@ -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) @@ -1,4 +1,4 @@ package: com.dropbear.ffi.generated headers: dropbear.h -headerFilter: *.h components/*.h +headerFilter: *.h libraryPaths: src @@ -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 + ) } @@ -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); } @@ -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, @@ -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()