kitgit

tirbofish/dropbear · commit

4f1d6503c81153cb7da418bbd5f4e3b5ea3fce0f

debugging?

Unverified

tk <4tkbytes@pm.me> · 2025-10-16 01:49

view full diff

diff --git a/build.gradle.kts b/build.gradle.kts
index 0b7a94f..d40bd31 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -92,7 +92,6 @@ kotlin {
     sourceSets {
         commonMain {
             dependencies {
-//                api("co.touchlab:kermit:2.0.4")
                 implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
             }
         }
diff --git a/docs/articles/debugging.md b/docs/articles/debugging.md
new file mode 100644
index 0000000..ba70e14
--- /dev/null
+++ b/docs/articles/debugging.md
@@ -0,0 +1,36 @@
+# Debugging your app
+
+There is an issue while you are developing your game such as your code is
+a bit funky, and you cannot seem to point it out yourself. Well there is no 
+need to fret. The eucalyptus-editor has a `jdb` enabled. 
+
+## How to connect
+
+You have two options, you can either use the command line [`jdb`] or 
+IntelliJ IDEA, which has Java Debugging available as one of its tasks. 
+
+In common, you would have to connect to this: 
+- IP Address: localhost
+- Port: 6741 *(haha very funny im so much comedian)*
+
+### jdb
+
+To connect to jdb, this is the recommended command: 
+
+```bash
+jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=6741
+```
+
+[//]: # (Don't ask why, but this one just works for me)
+
+### IntelliJ IDEA
+
+JetBrains IntelliJ IDEA is the best and recommended tool to use
+while in development. It contains debugging tools in a UI fashion,
+so it could help beginners too. 
+
+Here is the configuration:
+![img.png](images/img.png)
+
+---
+Enjoy your debugging and hopefully you solve all your issues. 
\ No newline at end of file
diff --git a/docs/articles/images/img.png b/docs/articles/images/img.png
new file mode 100644
index 0000000..2f5bc92
Binary files /dev/null and b/docs/articles/images/img.png differ
diff --git a/eucalyptus-core/Cargo.toml b/eucalyptus-core/Cargo.toml
index a96626c..d7fbc51 100644
--- a/eucalyptus-core/Cargo.toml
+++ b/eucalyptus-core/Cargo.toml
@@ -34,6 +34,7 @@ tokio-util = "0.7"
 app_dirs2.workspace = true
 
 [features]
+# editor only stuff
 editor = ["jvm"]
 jvm = []
 
diff --git a/eucalyptus-core/src/camera.rs b/eucalyptus-core/src/camera.rs
index 0bd70e6..b8140b6 100644
--- a/eucalyptus-core/src/camera.rs
+++ b/eucalyptus-core/src/camera.rs
@@ -1,8 +1,6 @@
 use dropbear_engine::camera::Camera;
 use glam::DVec3;
 use serde::{Deserialize, Serialize};
-use std::collections::HashSet;
-use winit::keyboard::KeyCode;
 
 #[derive(Debug, Clone)]
 pub struct CameraComponent {
@@ -50,30 +48,6 @@ impl PlayerCamera {
             ..CameraComponent::new()
         }
     }
-
-    pub fn handle_keyboard_input(camera: &mut Camera, pressed_keys: &HashSet<KeyCode>) {
-        for key in pressed_keys {
-            match key {
-                KeyCode::KeyW => camera.move_forwards(),
-                KeyCode::KeyA => camera.move_left(),
-                KeyCode::KeyD => camera.move_right(),
-                KeyCode::KeyS => camera.move_back(),
-                KeyCode::ShiftLeft => camera.move_down(),
-                KeyCode::Space => camera.move_up(),
-                _ => {}
-            }
-        }
-    }
-
-    pub fn handle_mouse_input(
-        camera: &mut Camera,
-        component: &CameraComponent,
-        mouse_delta: Option<(f64, f64)>,
-    ) {
-        if let Some((dx, dy)) = mouse_delta {
-            camera.track_mouse_delta(dx * component.sensitivity, dy * component.sensitivity);
-        }
-    }
 }
 
 pub struct DebugCamera;
diff --git a/eucalyptus-core/src/logging.rs b/eucalyptus-core/src/logging.rs
index a5196aa..3ddea33 100644
--- a/eucalyptus-core/src/logging.rs
+++ b/eucalyptus-core/src/logging.rs
@@ -77,7 +77,7 @@ macro_rules! fatal {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                 text: _msg.into(),
@@ -106,7 +106,7 @@ macro_rules! success {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                     text: _msg.into(),
@@ -136,7 +136,7 @@ macro_rules! warn {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                     text: _msg.into(),
@@ -165,7 +165,7 @@ macro_rules! info {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                 text: _msg.into(),
@@ -195,7 +195,7 @@ macro_rules! info_without_console {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                 text: _msg.into(),
@@ -225,7 +225,7 @@ macro_rules! success_without_console {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                 text: _msg.into(),
@@ -255,7 +255,7 @@ macro_rules! warn_without_console {
         #[cfg(feature = "editor")]
         {
             use egui_toast_fork::{Toast, ToastKind};
-            use eucalyptus_core::logging::GLOBAL_TOASTS;
+            use $crate::logging::GLOBAL_TOASTS;
             let mut toasts = GLOBAL_TOASTS.lock();
             toasts.add(Toast {
                 text: _msg.into(),
diff --git a/eucalyptus-core/src/scripting/jni.rs b/eucalyptus-core/src/scripting/jni.rs
index 8cb4ee2..fb0f0bd 100644
--- a/eucalyptus-core/src/scripting/jni.rs
+++ b/eucalyptus-core/src/scripting/jni.rs
@@ -1,16 +1,19 @@
 #![allow(non_snake_case)]
 //! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate
 
+pub mod exception;
+
 use dropbear_engine::entity::AdoptedEntity;
 use hecs::World;
-use jni::objects::{GlobalRef, JClass, JString, JValue};
+use jni::objects::{GlobalRef, JClass, JObject, JString, JThrowable, JValue, JValueGen};
 use jni::sys::jlong;
 use jni::sys::jobject;
 use jni::{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM};
 use std::path::{Path, PathBuf};
-use crate::APP_INFO;
+use crate::{info, APP_INFO};
 use crate::logging::{LOG_LEVEL};
 use crate::ptr::WorldPtr;
+use crate::scripting::jni::exception::get_exception_info;
 
 const LIBRARY_PATH: &[u8] = include_bytes!("../../../build/libs/dropbear-1.0-SNAPSHOT-all.jar");
 
@@ -45,11 +48,19 @@ impl JavaContext {
             .option(format!(
                 "-Djava.class.path={}",
                 host_jar_path.display()
-            ))
-            .option("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:6741")
-            .build()?;
+            ));
+
+        #[cfg(feature = "editor")]
+        let jvm_args = jvm_args
+            .option(
+                "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:6741"
+            );
+
+        let jvm_args = jvm_args.build()?;
         let jvm = JavaVM::new(jvm_args)?;
 
+        info!("JDB debugger enabled on localhost:6741");
+
         log::info!("Created JVM instance");
 
         Ok(Self {
@@ -77,6 +88,9 @@ impl JavaContext {
         log::trace!("Creating new instance of NativeEngine");
         let native_engine_obj = env.new_object(native_engine_class, "()V", &[])?;
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         let world_handle = world as jlong;
         log::trace!("Calling NativeEngine.init() with arg [{} as JValue::Long]", world_handle);
         env.call_method(
@@ -86,6 +100,9 @@ impl JavaContext {
             &[JValue::Long(world_handle)],
         )?;
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         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(
@@ -94,12 +111,15 @@ impl JavaContext {
             &[JValue::Object(&native_engine_obj)],
         )?;
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         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.clone());
 
         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_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,
@@ -107,6 +127,9 @@ impl JavaContext {
             "Lcom/dropbear/logging/LogLevel;"
         )?.l()?;
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         let std_out_writer_class = env.find_class("com/dropbear/logging/StdoutWriter")?;
         let log_writer_obj = env.new_object(std_out_writer_class, "()V", &[])?;
 
@@ -114,8 +137,14 @@ impl JavaContext {
         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 result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         let log_target_jstring = env.new_string("dropbear_rust_host")?;
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         let system_manager_obj = env.new_object(
             system_manager_class,
             "(Ljava/lang/String;Ljava/lang/Object;Lcom/dropbear/logging/LogWriter;Lcom/dropbear/logging/LogLevel;Ljava/lang/String;)V",
@@ -128,10 +157,16 @@ impl JavaContext {
             ],
         )?;
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         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);
 
+        let result = get_exception_info(&mut env);
+        if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
         Ok(())
     }
 
@@ -149,6 +184,9 @@ impl JavaContext {
                 "(Ljava/lang/String;)V",
                 &[JValue::Object(&jar_path_jstring)],
             )?;
+
+            let result = get_exception_info(&mut env);
+            if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
         } else {
             log::warn!("SystemManager instance not found during reload.");
             // self.init(world)?;
@@ -173,6 +211,9 @@ impl JavaContext {
                 &[JValue::Object(&tag_jstring)],
             )?;
 
+            let result = get_exception_info(&mut env);
+            if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
             log::debug!("Loaded systems for tag: {}", tag);
         } else {
             return Err(anyhow::anyhow!("SystemManager not initialised when loading systems for tag: {}", tag));
@@ -192,6 +233,9 @@ impl JavaContext {
                 &[JValue::Float(dt)],
             )?;
 
+            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);
         } else {
             return Err(anyhow::anyhow!("SystemManager not initialised when updating systems."));
@@ -212,6 +256,9 @@ impl JavaContext {
                 &[JValue::Object(&tag_jstring), JValue::Float(dt)],
             )?;
 
+            let result = get_exception_info(&mut env);
+            if result.is_some() { return Err(anyhow::anyhow!("{}", result.unwrap())); }
+
             log::debug!("Updated systems for tag: {} with dt: {}", tag, dt);
         } else {
             return Err(anyhow::anyhow!("SystemManager not initialised when updating systems for tag: {}", tag));
@@ -232,6 +279,9 @@ impl JavaContext {
                 &[JValue::Object(&tag_jstring)],
             )?;
 
+            let exception = get_exception_info(&mut env);
+            if exception.is_some() { return Err(anyhow::anyhow!("{}", exception.unwrap())); }
+
             Ok(result.i()?)
         } else {
             Err(anyhow::anyhow!("SystemManager not initialised when getting system count for tag: {}", tag))
@@ -251,6 +301,9 @@ impl JavaContext {
                 &[JValue::Object(&tag_jstring)],
             )?;
 
+            let exception = get_exception_info(&mut env);
+            if exception.is_some() { return Err(anyhow::anyhow!("{}", exception.unwrap())); }
+
             Ok(result.z()?)
         } else {
             Err(anyhow::anyhow!("SystemManager not initialised when checking for systems for tag: {}", tag))
@@ -269,6 +322,9 @@ impl JavaContext {
                 &[],
             )?;
 
+            let exception = get_exception_info(&mut env);
+            if exception.is_some() { return Err(anyhow::anyhow!("{}", exception.unwrap())); }
+
             Ok(result.i()?)
         } else {
             Err(anyhow::anyhow!("SystemManager not initialised when getting total system count."))
@@ -297,20 +353,38 @@ impl Drop for JavaContext {
     }
 }
 
-#[unsafe(no_mangle)]
-// JNIEXPORT jlong JNICALL Java_com_dropbear_ffi_JNINative_getEntity
-// (JNIEnv *, jobject, jlong, jstring);
+
+
 pub fn Java_com_dropbear_ffi_JNINative_getEntity(
-    env: &mut JNIEnv,
+    mut env: JNIEnv,
     _obj: jobject,
     world_handle: jlong,
     label: JString,
 ) -> jlong {
-    let label = env.get_string(&label).unwrap();
+    eprintln!("JNI call started");
+    eprintln!("world_handle: {}", world_handle);
+    eprintln!("label ptr: {:p}", label.as_raw());
+
+    if world_handle == 0 {
+        eprintln!("ERROR: world_handle is null!");
+        return -1;
+    }
+
+    let label_result = env.get_string(&label);
+    if label_result.is_err() {
+        eprintln!("ERROR: Failed to get string: {:?}", label_result.err());
+        return -1;
+    }
+    let label = label_result.unwrap();
+
     let world = world_handle as *mut World;
 
-    let world = unsafe { &mut *world };
+    if world.is_null() {
+        eprintln!("ERROR: world pointer is null!");
+        return -1;
+    }
 
+    let world = unsafe { &mut *world };
     let rust_label = label.to_str().unwrap().to_string();
 
     for (id, entity) in world.query::<&AdoptedEntity>().iter() {
diff --git a/eucalyptus-core/src/scripting/jni/exception.rs b/eucalyptus-core/src/scripting/jni/exception.rs
new file mode 100644
index 0000000..b7efccc
--- /dev/null
+++ b/eucalyptus-core/src/scripting/jni/exception.rs
@@ -0,0 +1,83 @@
+use std::error::Error;
+use jni::JNIEnv;
+use jni::objects::JThrowable;
+
+#[derive(Debug, Clone)]
+pub struct JavaExceptionInfo {
+    pub message: Option<String>,
+    pub class_name: String,
+    pub stack_trace: Option<String>,
+}
+
+impl std::fmt::Display for JavaExceptionInfo {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}: {}",
+               self.class_name,
+               self.message.as_deref().unwrap_or("<no message>")
+        )?;
+        if let Some(stack) = &self.stack_trace {
+            write!(f, "\n{}", stack)?;
+        }
+        Ok(())
+    }
+}
+
+pub fn get_exception_info(env: &mut JNIEnv) -> Option<JavaExceptionInfo> {
+    if !env.exception_check().ok()? {
+        return None;
+    }
+
+    let exception = env.exception_occurred().ok()?;
+
+    env.exception_clear().ok()?;
+
+    let exception_class = env.get_object_class(&exception).ok()?;
+    let class_obj = env.call_method(&exception_class, "getName", "()Ljava/lang/String;", &[]).ok()?;
+    let class_name_jstring = class_obj.l().ok()?;
+    let class_name = env.get_string((&class_name_jstring).into()).ok()?;
+    let class_name = class_name.to_string_lossy().to_string();
+
+    let message = env.call_method(&exception, "getMessage", "()Ljava/lang/String;", &[])
+        .ok()
+        .and_then(|m| m.l().ok())
+        .and_then(|m| {
+            if m.is_null() {
+                None
+            } else {
+                env.get_string((&m).into()).ok().map(|s| s.to_string_lossy().to_string())
+            }
+        });
+
+    let stack_trace = get_stack_trace(env, &exception);
+
+    Some(JavaExceptionInfo {
+        message,
+        class_name,
+        stack_trace,
+    })
+}
+
+fn get_stack_trace(env: &mut JNIEnv, exception: &JThrowable) -> Option<String> {
+    let string_writer_class = env.find_class("java/io/StringWriter").ok()?;
+    let string_writer = env.new_object(string_writer_class, "()V", &[]).ok()?;
+
+    let print_writer_class = env.find_class("java/io/PrintWriter").ok()?;
+    let print_writer = env.new_object(
+        print_writer_class,
+        "(Ljava/io/Writer;)V",
+        &[(&string_writer).into()]
+    ).ok()?;
+
+    env.call_method(
+        exception,
+        "printStackTrace",
+        "(Ljava/io/PrintWriter;)V",
+        &[(&print_writer).into()]
+    ).ok()?;
+
+    let stack_trace_obj = env.call_method(&string_writer, "toString", "()Ljava/lang/String;", &[]).ok()?;
+    let stack_trace_jstring = stack_trace_obj.l().ok()?;
+    let stack_trace = env.get_string((&stack_trace_jstring).into()).ok()?;
+
+    Some(stack_trace.to_string_lossy().to_string())
+}
\ No newline at end of file
diff --git a/eucalyptus-editor/src/editor/input.rs b/eucalyptus-editor/src/editor/input.rs
index 93fc611..68ad671 100644
--- a/eucalyptus-editor/src/editor/input.rs
+++ b/eucalyptus-editor/src/editor/input.rs
@@ -266,37 +266,37 @@ impl Mouse for Editor {
     #[cfg(not(target_os = "linux"))]
     // for some reason, this doesn't work on linux but works on windows (not tested on anywhere else)
     fn mouse_move(&mut self, position: PhysicalPosition<f64>) {
-        if ((self.is_viewport_focused && matches!(self.viewport_mode, ViewportMode::CameraMove))
-            || (matches!(self.editor_state, EditorState::Playing)
-                && !self.input_state.is_cursor_locked))
-            && let Some(window) = &self.window
-        {
-            let size = window.inner_size();
-            let center = PhysicalPosition::new(size.width as f64 / 2.0, size.height as f64 / 2.0);
-
-            let distance_from_center =
-                ((position.x - center.x).powi(2) + (position.y - center.y).powi(2)).sqrt();
-
-            if distance_from_center > 5.0 {
-                let dx = position.x - center.x;
-                let dy = position.y - center.y;
-
-                if let Some(active_camera) = *self.active_camera.lock()
-                    && let Ok(mut q) = self.world.query_one::<(
-                        &mut Camera,
-                        &CameraComponent,
-                        // Option<&CameraFollowTarget>,
-                    )>(active_camera)
-                    && let Some((camera, _)) = q.get()
-                {
-                    camera.track_mouse_delta(dx, dy);
-                }
-
-                let _ = window.set_cursor_position(center);
-            }
-
-            window.set_cursor_visible(false);
-        }
+        // if ((self.is_viewport_focused && matches!(self.viewport_mode, ViewportMode::CameraMove))
+        //     || (matches!(self.editor_state, EditorState::Playing)
+        //         && !self.input_state.is_cursor_locked))
+        //     && let Some(window) = &self.window
+        // {
+        //     let size = window.inner_size();
+        //     let center = PhysicalPosition::new(size.width as f64 / 2.0, size.height as f64 / 2.0);
+        //
+        //     let distance_from_center =
+        //         ((position.x - center.x).powi(2) + (position.y - center.y).powi(2)).sqrt();
+        //
+        //     if distance_from_center > 5.0 {
+        //         let dx = position.x - center.x;
+        //         let dy = position.y - center.y;
+        //
+        //         if let Some(active_camera) = *self.active_camera.lock()
+        //             && let Ok(mut q) = self.world.query_one::<(
+        //                 &mut Camera,
+        //                 &CameraComponent,
+        //                 // Option<&CameraFollowTarget>,
+        //             )>(active_camera)
+        //             && let Some((camera, _)) = q.get()
+        //         {
+        //             camera.track_mouse_delta(dx, dy);
+        //         }
+        //
+        //         let _ = window.set_cursor_position(center);
+        //     }
+        //
+        //     window.set_cursor_visible(false);
+        // }
 
         self.input_state.mouse_pos = (position.x, position.y);
     }