kitgit

tirbofish/dropbear · commit

563bf9a1ba928e93134295c4996390ecf14398e3

added new native functions for player manipulation specifics: - JNINative.getEntity now queries and searches - JNINative.getTransform now fetches transforms successfully - JNINative.setTransform now sets transform for that specific entity.

Unverified

tk <4tkbytes@pm.me> · 2025-10-16 13:41

view full diff

diff --git a/README.md b/README.md
index 82fb88e..f2f392e 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ git clone git@github.com:4tkbytes/dropbear
 cd dropbear
 
 # eucalyptus-editor requires dropbear-1.0-SNAPSHOT-all.jar to be built first
-./gradlew build
+./gradlew build fatJar
 # this will build all the projects in the workspace
 cargo build
 ```
diff --git a/eucalyptus-core/src/scripting/jni.rs b/eucalyptus-core/src/scripting/jni.rs
index e321b1b..86b7194 100644
--- a/eucalyptus-core/src/scripting/jni.rs
+++ b/eucalyptus-core/src/scripting/jni.rs
@@ -4,13 +4,13 @@
 pub mod exception;
 
 use std::fs;
-use dropbear_engine::entity::AdoptedEntity;
+use dropbear_engine::entity::{AdoptedEntity, Transform};
 use hecs::World;
-use jni::objects::{GlobalRef, JClass, JString, JValue};
-use jni::sys::jlong;
-use jni::sys::jobject;
+use jni::objects::{GlobalRef, JClass, JObject, JString, JValue};
+use jni::sys::{jclass, jlong};
 use jni::{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM};
 use std::path::{PathBuf};
+use glam::{DQuat, DVec3};
 use crate::{success, APP_INFO};
 use sha2::{Digest, Sha256};
 use crate::logging::{LOG_LEVEL};
@@ -267,7 +267,7 @@ impl JavaContext {
             let result = get_exception_info(&mut env);
             if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
 
-            log::debug!("Updated all systems with dt: {}", dt);
+            log::trace!("Updated all systems with dt: {}", dt);
         } else {
             return Err(anyhow::anyhow!("SystemManager not initialised when updating systems."));
         }
@@ -384,12 +384,12 @@ impl Drop for JavaContext {
     }
 }
 
-#[unsafe(no_mangle)]
 // JNIEXPORT jlong JNICALL Java_com_dropbear_ffi_JNINative_getEntity
-// (JNIEnv *, jobject, jlong, jstring);
+//   (JNIEnv *, jclass, jlong, jstring);
+#[unsafe(no_mangle)]
 pub fn Java_com_dropbear_ffi_JNINative_getEntity(
     mut env: JNIEnv,
-    _obj: jobject,
+    _obj: jclass,
     world_handle: jlong,
     label: JString,
 ) -> jlong {
@@ -413,7 +413,7 @@ pub fn Java_com_dropbear_ffi_JNINative_getEntity(
     let world = world_handle as *mut World;
 
     if world.is_null() {
-        println!("[Java_com_dropbear_ffi_JNINative_getEntity] [ERROR] World pointer is null in Java_com_dropbear_ffi_JNINative_getEntity");
+        println!("[Java_com_dropbear_ffi_JNINative_getEntity] [ERROR] World pointer is null");
         return -1;
     }
 
@@ -425,4 +425,159 @@ pub fn Java_com_dropbear_ffi_JNINative_getEntity(
         }
     }
     -1
+}
+
+// JNIEXPORT jobject JNICALL Java_com_dropbear_ffi_JNINative_getTransform
+//   (JNIEnv *, jclass, jlong, jlong);
+#[unsafe(no_mangle)]
+pub fn Java_com_dropbear_ffi_JNINative_getTransform(
+    mut env: JNIEnv,
+    _class: jclass,
+    world_handle: jlong,
+    entity_id: jlong,
+) -> JObject {
+    let world = world_handle as *mut World;
+
+    if world.is_null() {
+        println!("[Java_com_dropbear_ffi_JNINative_getTransform] [ERROR] World pointer is null");
+        return JObject::null();
+    }
+
+    let world = unsafe { &mut *world };
+
+    let entity = unsafe { world.find_entity_from_id(entity_id as u32) };
+
+    if let Ok(mut q) = world.query_one::<(&AdoptedEntity, &Transform)>(entity)
+        && let Some((_, transform)) = q.get()
+    {
+        let new_transform = *transform;
+
+        let transform_class = match env.find_class("com/dropbear/math/Transform") {
+            Ok(c) => c,
+            Err(_) => return JObject::null(),
+        };
+
+        return match env.new_object(
+            &transform_class,
+            "(DDDDDDDDDD)V",
+            &[
+                new_transform.position.x.into(),
+                new_transform.position.y.into(),
+                new_transform.position.z.into(),
+                new_transform.rotation.x.into(),
+                new_transform.rotation.y.into(),
+                new_transform.rotation.z.into(),
+                new_transform.rotation.w.into(),
+                new_transform.scale.x.into(),
+                new_transform.scale.y.into(),
+                new_transform.scale.z.into(),
+            ],
+        ) {
+            Ok(java_transform) => java_transform,
+            Err(_) => {
+                println!("[Java_com_dropbear_ffi_JNINative_getTransform] [ERROR] Failed to create Transform object");
+                JObject::null()
+            }
+        };
+    }
+
+    println!("[Java_com_dropbear_ffi_JNINative_getTransform] [ERROR] Failed to query for transform value for entity: {}", entity_id);
+    JObject::null()
+}
+
+// JNIEXPORT void JNICALL Java_com_dropbear_ffi_JNINative_setTransform
+//   (JNIEnv *, jclass, jlong, jlong, jobject);
+#[unsafe(no_mangle)]
+pub fn Java_com_dropbear_ffi_JNINative_setTransform(
+    mut env: JNIEnv,
+    _class: jclass,
+    world_handle: jlong,
+    entity_id: jlong,
+    transform_obj: JObject,
+) {
+    let world = world_handle as *mut World;
+
+    if world.is_null() {
+        println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] World pointer is null");
+        return;
+    }
+
+    let world = unsafe { &mut *world };
+    let entity = unsafe { world.find_entity_from_id(entity_id as u32) };
+
+    let get_number_field = |env: &mut JNIEnv, obj: &JObject, field_name: &str| -> f64 {
+        match env.get_field(obj, field_name, "Ljava/lang/Number;") {
+            Ok(v) => match v.l() {
+                Ok(num_obj) => {
+                    match env.call_method(&num_obj, "doubleValue", "()D", &[]) {
+                        Ok(result) => result.d().unwrap_or_else(|_| {
+                            println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to extract double from {}", field_name);
+                            0.0
+                        }),
+                        Err(_) => {
+                            println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to call doubleValue on {}", field_name);
+                            0.0
+                        }
+                    }
+                }
+                Err(_) => {
+                    println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to extract Number object for {}", field_name);
+                    0.0
+                }
+            },
+            Err(_) => {
+                println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to get field {}", field_name);
+                0.0
+            }
+        }
+    };
+
+    let position_obj: JObject = match env.get_field(&transform_obj, "position", "Lcom/dropbear/math/Vector3;") {
+        Ok(v) => v.l().unwrap_or_else(|_| JObject::null()),
+        Err(_) => JObject::null(),
+    };
+
+    let rotation_obj: JObject = match env.get_field(&transform_obj, "rotation", "Lcom/dropbear/math/Quaternion;") {
+        Ok(v) => v.l().unwrap_or_else(|_| JObject::null()),
+        Err(_) => JObject::null(),
+    };
+
+    let scale_obj: JObject = match env.get_field(&transform_obj, "scale", "Lcom/dropbear/math/Vector3;") {
+        Ok(v) => v.l().unwrap_or_else(|_| JObject::null()),
+        Err(_) => JObject::null(),
+    };
+
+    if position_obj.is_null() || rotation_obj.is_null() || scale_obj.is_null() {
+        println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to extract position/rotation/scale objects");
+        return;
+    }
+
+    let px = get_number_field(&mut env, &position_obj, "x");
+    let py = get_number_field(&mut env, &position_obj, "y");
+    let pz = get_number_field(&mut env, &position_obj, "z");
+
+    let rx = get_number_field(&mut env, &rotation_obj, "x");
+    let ry = get_number_field(&mut env, &rotation_obj, "y");
+    let rz = get_number_field(&mut env, &rotation_obj, "z");
+    let rw = get_number_field(&mut env, &rotation_obj, "w");
+
+    let sx = get_number_field(&mut env, &scale_obj, "x");
+    let sy = get_number_field(&mut env, &scale_obj, "y");
+    let sz = get_number_field(&mut env, &scale_obj, "z");
+
+    let new_transform = Transform {
+        position: DVec3::new(px, py, pz),
+        rotation: DQuat::from_axis_angle(DVec3::new(rx, ry, rz), rw),
+        scale: DVec3::new(sx, sy, sz),
+    };
+
+    if let Ok(mut q) = world.query_one::<&mut Transform>(entity) {
+        if let Some(transform) = q.get() {
+            *transform = new_transform;
+        } else {
+            println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to query for transform component");
+        }
+    } else {
+        println!("[Java_com_dropbear_ffi_JNINative_setTransform] [ERROR] Failed to query entity for transform component");
+    }
 }
\ No newline at end of file
diff --git a/eucalyptus-editor/src/editor/scene.rs b/eucalyptus-editor/src/editor/scene.rs
index 4be71e3..3773412 100644
--- a/eucalyptus-editor/src/editor/scene.rs
+++ b/eucalyptus-editor/src/editor/scene.rs
@@ -123,6 +123,7 @@ impl Scene for Editor {
                     "Failed to update script: {}",
                     e
                 );
+                self.signal = Signal::StopPlaying;
             }
         }
 
diff --git a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
index 320912e..f40473a 100644
--- a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
@@ -1,18 +1,22 @@
 package com.dropbear
 
 import com.dropbear.ffi.NativeEngine
+import com.dropbear.math.Transform
 
 class DropbearEngine(val native: NativeEngine) {
-
-    fun getEntity(label: String): Result<EntityRef> {
+    public fun getEntity(label: String): EntityRef? {
         val entityId = native.getEntity(label)
-        return (when (entityId) {
-            null -> {
-                Result.failure(Exception("JNI returned null"))
-            }
-            else -> {
-                Result.success(EntityRef(EntityId(entityId)))
-            }
-        })
+        val entityRef = if (entityId != null) EntityRef(EntityId(entityId)) else null
+        entityRef?.engine = this
+        return entityRef
+    }
+
+    internal fun getTransform(entityId: EntityId): Transform? {
+        val result = native.getTransform(entityId)
+        return result
+    }
+
+    internal fun setTransform(entityId: EntityId, transform: Transform) {
+        native.setTransform(entityId, transform)
     }
 }
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/EntityRef.kt b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
index 1d2e355..48b09ec 100644
--- a/src/commonMain/kotlin/com/dropbear/EntityRef.kt
+++ b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
@@ -1,25 +1,20 @@
 package com.dropbear
 
-import com.dropbear.math.Vector3D
+import com.dropbear.math.Transform
 
-/**
- * A class to hold a reference to an entity.
- *
- * The dropbear engine interface is made in Rust, which uses a
- * borrow checker paradigm, which heavily utilises immutability
- * unless explicitly provided with the `mut` keyword.
- *
- * Because of this, the primary source of the World (place to store
- * entities) is stored in Rust, and passing an EntityRef (which contains
- * an ID) follows Rust's ideologies of passing references instead of the entire entity,
- * which provides immutability.
- *
- * To edit any values part of the entity, take a look at the functions provided
- * by [EntityRef], which will require a reference to [DropbearEngine] to push the commands.
- */
 class EntityRef(val id: EntityId = EntityId(0L)) {
+    lateinit var engine: DropbearEngine
+
     override fun toString(): String {
         return "EntityRef(id=$id)"
     }
-    fun setPosition(position: Vector3D, engine: DropbearEngine) {}
+
+    fun getTransform(): Transform? {
+        return engine.getTransform(id)
+    }
+
+    fun setTransform(transform: Transform?) {
+        if (transform == null) return
+        return engine.setTransform(id, transform)
+    }
 }
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/Example.kt b/src/commonMain/kotlin/com/dropbear/Example.kt
new file mode 100644
index 0000000..d25ebba
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/Example.kt
@@ -0,0 +1,20 @@
+import com.dropbear.DropbearEngine
+import com.dropbear.Runnable
+import com.dropbear.System
+
+@Runnable
+class Example: System() {
+    override fun load(engine: DropbearEngine) {
+        val entity = engine.getEntity("example")
+        val transform = entity?.getTransform()
+        transform?.position?.x = 10.0
+        entity?.setTransform(transform)
+    }
+
+    override fun update(engine: DropbearEngine, deltaTime: Float) {
+    }
+
+    override fun destroy(engine: DropbearEngine) {
+
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
index 266302a..4c89991 100644
--- a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
@@ -1,5 +1,10 @@
 package com.dropbear.ffi
 
+import com.dropbear.EntityId
+import com.dropbear.math.Transform
+
 expect class NativeEngine {
     fun getEntity(label: String): Long?
+    fun getTransform(entityId: EntityId): Transform?
+    fun setTransform(entityId: EntityId, transform: Transform)
 }
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt b/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
index 97779eb..85f6c6c 100644
--- a/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
+++ b/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
@@ -1,8 +1,15 @@
 package com.dropbear.math
 
+import kotlin.jvm.JvmField
+
 typealias QuaternionD = Quaternion<Double>
 
-class Quaternion<T: Number>(var x: T, var y: T, var z: T, var w: T) {
+class Quaternion<T: Number>(
+    @JvmField var x: T,
+    @JvmField var y: T,
+    @JvmField var z: T,
+    @JvmField var w: T
+) {
     companion object {
         fun identity(): Quaternion<Double> {
             return Quaternion(0.0, 0.0, 0.0, 1.0)
diff --git a/src/commonMain/kotlin/com/dropbear/math/Transform.kt b/src/commonMain/kotlin/com/dropbear/math/Transform.kt
new file mode 100644
index 0000000..a46a56a
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/math/Transform.kt
@@ -0,0 +1,20 @@
+package com.dropbear.math
+
+class Transform(
+    var position: Vector3D,
+    var rotation: QuaternionD,
+    var scale: Vector3D
+) {
+    constructor(px: Double, py: Double, pz: Double,
+                rx: Double, ry: Double, rz: Double, rw: Double,
+                sx: Double, sy: Double, sz: Double)
+            : this(
+        Vector3D(px, py, pz),
+        QuaternionD(rx, ry, rz, rw),
+        Vector3D(sx, sy, sz)
+            )
+
+    override fun toString(): String {
+        return "Transform(position=$position, rotation=$rotation, scale=$scale)"
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/math/Vector3.kt b/src/commonMain/kotlin/com/dropbear/math/Vector3.kt
index 822dd23..57dc03f 100644
--- a/src/commonMain/kotlin/com/dropbear/math/Vector3.kt
+++ b/src/commonMain/kotlin/com/dropbear/math/Vector3.kt
@@ -1,9 +1,15 @@
 package com.dropbear.math
 
+import kotlin.jvm.JvmField
+
 /**
  * A class for holding a vector of `3` values of the same type.
  */
-class Vector3<T: Number>(var x: T, var y: T, var z: T) {
+class Vector3<T: Number>(
+    @JvmField var x: T,
+    @JvmField var y: T,
+    @JvmField var z: T,
+) {
     companion object {
         /**
          * Creates a new [com.dropbear.math.Vector3] of type `T` with one value.
diff --git a/src/jvmMain/java/com/dropbear/ffi/JNINative.java b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
index 6429413..7968c49 100644
--- a/src/jvmMain/java/com/dropbear/ffi/JNINative.java
+++ b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
@@ -1,9 +1,14 @@
 package com.dropbear.ffi;
 
+import com.dropbear.math.Transform;
+
 public class JNINative {
     static {
         System.loadLibrary("eucalyptus_core");
     }
 
     public static native long getEntity(long handle, String label);
+
+    public static native Transform getTransform(long handle, long entityHandle);
+    public static native void setTransform(long worldHandle, long id, Transform transform);
 }
diff --git a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
index 7e1843c..49a0101 100644
--- a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
+++ b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
@@ -1,15 +1,13 @@
 package com.dropbear.ffi
 
+import com.dropbear.EntityId
+import com.dropbear.math.Transform
+
 actual class NativeEngine {
     private var worldHandle: Long = 0L
 
     actual fun getEntity(label: String): Long? {
-        val result = JNINative.getEntity(worldHandle, label)
-        return if (result < 0) {
-            null
-        } else {
-            result
-        }
+        return JNINative.getEntity(worldHandle, label)
     }
 
     @JvmName("init")
@@ -20,4 +18,12 @@ actual class NativeEngine {
             return
         }
     }
+
+    actual fun getTransform(entityId: EntityId): Transform? {
+        return JNINative.getTransform(worldHandle, entityId.id)
+    }
+
+    actual fun setTransform(entityId: EntityId, transform: Transform) {
+        return JNINative.setTransform(worldHandle, entityId.id, transform)
+    }
 }
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
index faadbe6..61f55a9 100644
--- a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
+++ b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
@@ -3,8 +3,10 @@
 
 package com.dropbear.ffi
 
+import com.dropbear.EntityId
 import com.dropbear.ffi.generated.dropbear_get_entity
 import com.dropbear.logging.Logger
+import com.dropbear.math.Transform
 import kotlinx.cinterop.*
 import kotlin.experimental.ExperimentalNativeApi
 
@@ -31,4 +33,11 @@ actual class NativeEngine {
             return if (result == 0) outEntity.value else null
         }
     }
+
+    actual fun getTransform(entityId: EntityId): Transform? {
+        TODO("Not yet implemented")
+    }
+
+    actual fun setTransform(entityId: EntityId, transform: Transform) {
+    }
 }
\ No newline at end of file