tirbofish/dropbear · commit
4f1d6503c81153cb7da418bbd5f4e3b5ea3fce0f
debugging?
Signature present but could not be verified.
Unverified
@@ -92,7 +92,6 @@ kotlin { sourceSets { commonMain { dependencies { -// api("co.touchlab:kermit:2.0.4") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0") } } @@ -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: + + +--- +Enjoy your debugging and hopefully you solve all your issues. Binary files /dev/null and b/docs/articles/images/img.png differ @@ -34,6 +34,7 @@ tokio-util = "0.7" app_dirs2.workspace = true [features] +# editor only stuff editor = ["jvm"] jvm = [] @@ -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; @@ -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(), @@ -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() { @@ -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()) +} @@ -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); }