From 2d4736f12e0bb1861796fb3fca14cd5634395af4 Mon Sep 17 00:00:00 2001 From: Chance Date: Sun, 30 Mar 2025 02:54:07 -0400 Subject: [PATCH] show dialog on panic --- Cargo.lock | 153 ++++++++++++++++++++++++++++++++++-- engine/Cargo.toml | 1 + engine/src/core/ecs/mod.rs | 157 +++++++++++++++++++++++++++++++++++++ engine/src/core/panic.rs | 12 ++- engine/src/main.rs | 1 + flake.nix | 1 + 6 files changed, 319 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1531833..9f31e91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -351,6 +357,36 @@ dependencies = [ "error-code", ] +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1101,6 +1137,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "jni" version = "0.21.1" @@ -1277,6 +1322,12 @@ dependencies = [ "paste", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.5" @@ -1308,6 +1359,29 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "native-dialog" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e7038885d2aeab236bd60da9e159a5967b47cde3292da3b15ff1bec27c039f" +dependencies = [ + "ascii", + "block", + "cocoa", + "core-foundation", + "dirs-next", + "objc", + "objc-foundation", + "objc_id", + "once_cell", + "raw-window-handle 0.5.2", + "thiserror 1.0.69", + "versions", + "wfd", + "which", + "winapi", +] + [[package]] name = "ndk" version = "0.9.0" @@ -1319,7 +1393,7 @@ dependencies = [ "log", "ndk-sys 0.6.0+11769913", "num_enum", - "raw-window-handle", + "raw-window-handle 0.6.2", "thiserror 1.0.69", ] @@ -1368,6 +1442,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1417,6 +1501,17 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -1620,6 +1715,15 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.36.7" @@ -1868,6 +1972,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -2555,6 +2665,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "versions" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd" +dependencies = [ + "itertools", + "nom", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -2771,6 +2891,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wfd" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "wgpu" version = "24.0.3" @@ -2786,7 +2916,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.6.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -2814,7 +2944,7 @@ dependencies = [ "once_cell", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.6.2", "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.12", @@ -2856,7 +2986,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.6.2", "renderdoc-sys", "rustc-hash 1.1.0", "smallvec", @@ -2892,6 +3022,18 @@ dependencies = [ "wgpu", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3237,7 +3379,7 @@ dependencies = [ "orbclient", "percent-encoding", "pin-project", - "raw-window-handle", + "raw-window-handle 0.6.2", "redox_syscall 0.4.1", "rustix 0.38.44", "sctk-adwaita", @@ -3397,6 +3539,7 @@ dependencies = [ "dirs-next", "futures", "lazy_static", + "native-dialog", "once_cell", "parking_lot", "regex", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 62aaf9d..2e11738 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -42,3 +42,4 @@ typenum = { version = "1.18.0", features = ["const-generics"] } tobj = { version = "4.0.3", features = ["tokio"] } ahash = "0.8.11" wgpu_text = "0.9.2" +native-dialog = "0.7.0" diff --git a/engine/src/core/ecs/mod.rs b/engine/src/core/ecs/mod.rs index bfd93f0..ae72ce5 100644 --- a/engine/src/core/ecs/mod.rs +++ b/engine/src/core/ecs/mod.rs @@ -1,3 +1,7 @@ +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::sync::Mutex; + pub trait Component: Sized + 'static { fn update(&mut self, delta_time: f32); fn serialize(&self) -> Vec; @@ -11,3 +15,156 @@ pub trait Entity: Sized { fn serialize(&self) -> Vec; fn deserialize(data: &[u8; 6]) -> Self; } +lazy_static::lazy_static! { + // Global registry mapping component TypeId to a unique bit flag. + static ref COMPONENT_REGISTRY: Mutex> = Mutex::new(HashMap::new()); + static ref NEXT_COMPONENT_BIT: Mutex = Mutex::new(1); +} + +// To allow dynamic dispatch on components (even though Component itself is not object‐safe) +// we wrap them in an object–safe trait. +pub trait ComponentObject: Any { + fn update_obj(&mut self, delta_time: f32); + fn serialize_obj(&self) -> Vec; + fn as_any(&self) -> &dyn Any; +} +impl ComponentObject for T { + fn update_obj(&mut self, delta_time: f32) { + T::update(self, delta_time) + } + fn serialize_obj(&self) -> Vec { + T::serialize(self) + } + fn as_any(&self) -> &dyn Any { + self + } +} + +pub struct EntityImpl { + id: usize, + bitmask: u64, + // The key is the unique bit flag for the component type. + components: HashMap>, +} + +impl EntityImpl { + pub fn new(id: usize) -> Self { + EntityImpl { + id, + bitmask: 0, + components: HashMap::new(), + } + } +} + +impl Entity for EntityImpl { + fn add_component(&mut self, component: C) { + let type_id = TypeId::of::(); + let mut registry = COMPONENT_REGISTRY.lock().unwrap(); + let bit = registry.entry(type_id).or_insert_with(|| { + let mut next_bit = NEXT_COMPONENT_BIT.lock().unwrap(); + let current = *next_bit; + *next_bit *= 2; + current + }); + self.bitmask |= *bit; + self.components.insert(*bit, Box::new(component)); + } + + fn remove_component(&mut self) { + let type_id = TypeId::of::(); + if let Some(&bit) = COMPONENT_REGISTRY.lock().unwrap().get(&type_id) { + self.bitmask &= !bit; + } + } + + fn get_component(&self) -> Option<&C> { + let type_id = TypeId::of::(); + if let Some(&bit) = COMPONENT_REGISTRY.lock().unwrap().get(&type_id) { + self.components.get(&bit) + .and_then(|boxed| boxed.as_any().downcast_ref::()) + } else { + None + } + } + + fn serialize(&self) -> Vec { + // Serialize the entity's bitmask into 6 bytes (lowest 48 bits). + let mut bytes = self.bitmask.to_le_bytes().to_vec(); + bytes.truncate(6); + bytes + } + + fn deserialize(data: &[u8; 6]) -> Self { + let mut full = [0u8; 8]; + full[..6].copy_from_slice(data); + let bitmask = u64::from_le_bytes(full); + // When deserializing, we recreate an entity with the restored bitmask. + // Note: The individual component data are not restored here. + Self { + id: 0, + bitmask, + components: HashMap::new(), + } + } + +} + +pub struct ECS { + next_entity_id: usize, + pub entities: HashMap, +} + +impl ECS { + pub fn new() -> Self { + ECS { + next_entity_id: 0, + entities: HashMap::new(), + } + } + + pub fn create_entity(&mut self) -> &mut EntityImpl { + let entity = EntityImpl::new(self.next_entity_id); + self.entities.insert(self.next_entity_id, entity); + self.next_entity_id += 1; + self.entities.get_mut(&(self.next_entity_id - 1)).unwrap() + } + + pub fn update(&mut self, delta_time: f32) { + for entity in self.entities.values_mut() { + // Update each component attached to the entity. + for comp in entity.components.values_mut() { + comp.update_obj(delta_time); + } + } + } + + pub fn serialize(&self) -> Vec { + let mut data = Vec::new(); + // For each entity, store its id (8 bytes) and its 6-byte bitmask. + for (id, entity) in &self.entities { + data.extend_from_slice(&id.to_le_bytes()); + data.extend_from_slice(&entity.serialize()); + } + data + } + + pub fn deserialize(&mut self, data: &[u8]) { + self.entities.clear(); + // Each serialized entity uses 8 (id) + 6 (bitmask) = 14 bytes. + let entity_size = 14; + let count = data.len() / entity_size; + for i in 0..count { + let offset = i * entity_size; + let mut id_bytes = [0u8; 8]; + id_bytes.copy_from_slice(&data[offset..offset + 8]); + let id = usize::from_le_bytes(id_bytes); + + let mut mask_bytes = [0u8; 6]; + mask_bytes.copy_from_slice(&data[offset + 8..offset + 14]); + let entity = EntityImpl::deserialize(&mask_bytes); + self.entities.insert(id, entity); + } + self.next_entity_id = count; + } +} \ No newline at end of file diff --git a/engine/src/core/panic.rs b/engine/src/core/panic.rs index db207f6..25dba4d 100644 --- a/engine/src/core/panic.rs +++ b/engine/src/core/panic.rs @@ -3,8 +3,10 @@ use std::fmt::Write as FmtWrite; use std::mem; use backtrace::Backtrace; +use native_dialog::{MessageDialog, MessageType}; use parking_lot::Once; use regex::Regex; +use tracing::error; static INIT: parking_lot::Once = Once::new(); @@ -48,7 +50,7 @@ fn process_panic(info: &std::panic::PanicHookInfo<'_>) -> Result<(), Box anyhow::Result<()> { info!("{}", "Debug mode disabled".bright_blue()); set_panic_hook(); } + set_panic_hook(); setup(); let repl_thread = std::thread::spawn(|| { diff --git a/flake.nix b/flake.nix index 49c15bc..b51f286 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,7 @@ xorg.libX11 xorg.libxcb pkg-config + kdePackages.kdialog ]; in { packages = {