improve error handling

This commit is contained in:
Chance 2025-04-01 18:05:17 -04:00 committed by BitSyndicate
parent cb7a5058dc
commit 0b32f9ec0c
Signed by: bitsyndicate
GPG key ID: 443E4198D6BBA6DE
5 changed files with 112 additions and 54 deletions

View file

@ -81,7 +81,8 @@ impl Entity for EntityImpl {
fn get_component<C: Component>(&self) -> Option<&C> { fn get_component<C: Component>(&self) -> Option<&C> {
let type_id = TypeId::of::<C>(); let type_id = TypeId::of::<C>();
if let Some(&bit) = COMPONENT_REGISTRY.lock().unwrap().get(&type_id) { if let Some(&bit) = COMPONENT_REGISTRY.lock().unwrap().get(&type_id) {
self.components.get(&bit) self.components
.get(&bit)
.and_then(|boxed| boxed.as_any().downcast_ref::<C>()) .and_then(|boxed| boxed.as_any().downcast_ref::<C>())
} else { } else {
None None
@ -107,7 +108,6 @@ impl Entity for EntityImpl {
components: HashMap::new(), components: HashMap::new(),
} }
} }
} }
pub struct ECS { pub struct ECS {
@ -167,4 +167,4 @@ impl ECS {
} }
self.next_entity_id = count; self.next_entity_id = count;
} }
} }

View file

@ -48,33 +48,39 @@ fn process_panic(info: &std::panic::PanicHookInfo<'_>) -> Result<(), Box<dyn Err
writeln!(file, "{}", render_backtrace().sanitize_path())?; writeln!(file, "{}", render_backtrace().sanitize_path())?;
let panic_msg = format!( let panic_msg = format!(
"Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report. r#"Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report.
We have generated a report file at '{}'. Submit an issue or email with the subject of 'Zenyx Crash Report' and include the report as an attachment. We have generated a report file at '{}'. Submit an issue or email with the subject of 'Zenyx Crash Report' and include the report as an attachment.
To submit the crash report: To submit the crash report:
https://github.com/Zenyx-Engine/Zenyx/issues https://github.com/Zenyx-Engine/Zenyx/issues
We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports. We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports.
Thank you kindly!", log_path.display()); Thank you kindly!"#,
println!("{}", panic_msg.red().bold()); log_path.display()
println!(
"\nFor future reference, the error summary is as follows:\n{}",
payload_str.red().bold()
); );
if let Err(e ) = MessageDialog::new()
.set_type(MessageType::Error)
.set_title(&format!("{}",payload_str))
// wont display properly if not debug formatted??
.set_text(&format!("{:#?}", panic_msg))
.show_alert() {
error!("{e}");
}
Ok(())
}
let final_msg = format!(
r#"{}
For future reference, the error summary is as follows:
{}"#,
panic_msg, payload_str
);
println!("{}", final_msg.red().bold());
MessageDialog::new()
.set_type(MessageType::Error)
.set_title("A fatal error in Zenyx has occurred")
.set_text(&final_msg)
.show_alert()
.map_err(|e| {
error!("Failed to show error dialog: {}", e);
e.into()
})
}
fn render_backtrace() -> String { fn render_backtrace() -> String {
const HEX_WIDTH: usize = mem::size_of::<usize>() * 2 + 2; const HEX_WIDTH: usize = mem::size_of::<usize>() * 2 + 2;
const NEXT_SYMBOL_PADDING: usize = HEX_WIDTH + 6; const NEXT_SYMBOL_PADDING: usize = HEX_WIDTH + 6;

View file

@ -6,7 +6,7 @@ use std::time::Instant;
use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective}; use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective};
use futures::executor::block_on; use futures::executor::block_on;
use thiserror::Error; use thiserror::Error;
use tracing::{error, info, trace}; use tracing::{debug, error, info, trace};
use wgpu::TextureUsages; use wgpu::TextureUsages;
use wgpu::{Backends, InstanceDescriptor, util::DeviceExt}; use wgpu::{Backends, InstanceDescriptor, util::DeviceExt};
use wgpu_text::glyph_brush::ab_glyph::FontRef; use wgpu_text::glyph_brush::ab_glyph::FontRef;
@ -333,7 +333,7 @@ struct FontState {
impl<'window> Renderer<'window> { impl<'window> Renderer<'window> {
pub async fn new(window: Arc<Window>) -> Result<Self, RenderContextError> { pub async fn new(window: Arc<Window>) -> Result<Self, RenderContextError> {
let instance = wgpu::Instance::new(&InstanceDescriptor { let instance = wgpu::Instance::new(&InstanceDescriptor {
backends: Backends::from_comma_list("dx12,metal,opengl,webgpu"), backends: Backends::from_comma_list("dx12,metal,opengl,webgpu,vulkan"),
..Default::default() ..Default::default()
}); });
@ -458,23 +458,31 @@ impl<'window> Renderer<'window> {
let camera = Camera::new(&device, &camera_bind_group_layout, width, height); let camera = Camera::new(&device, &camera_bind_group_layout, width, height);
let surface_caps = surface.get_capabilities(&adapter); let surface_caps = surface.get_capabilities(&adapter);
let present_mode = [
wgpu::PresentMode::Immediate,
wgpu::PresentMode::Mailbox,
wgpu::PresentMode::AutoNoVsync,
]
.iter()
.copied()
.find(|mode| surface_caps.present_modes.contains(mode))
.unwrap_or(wgpu::PresentMode::Fifo);
debug!("Using {:#?} present mode.", present_mode);
let surface_config = wgpu::SurfaceConfiguration { let surface_config = wgpu::SurfaceConfiguration {
width, width,
height, height,
format: surface_caps.formats[0], format: surface_caps.formats[0],
present_mode: wgpu::PresentMode::AutoNoVsync, present_mode,
alpha_mode: wgpu::CompositeAlphaMode::Auto, alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![], view_formats: vec![],
usage: TextureUsages::RENDER_ATTACHMENT, usage: TextureUsages::RENDER_ATTACHMENT,
desired_maximum_frame_latency: 3, desired_maximum_frame_latency: 3,
}; };
surface.configure(&device, &surface_config); surface.configure(&device, &surface_config);
let (depth_texture, depth_texture_view) = create_depth_texture( let (depth_texture, depth_texture_view) =
&device, create_depth_texture(&device, surface_config.width, surface_config.height);
surface_config.width,
surface_config.height,
// surface_config.format,
);
let font_bytes = include_bytes!("DejaVuSans.ttf"); let font_bytes = include_bytes!("DejaVuSans.ttf");
let font = FontRef::try_from_slice(font_bytes).map_err(|e| { let font = FontRef::try_from_slice(font_bytes).map_err(|e| {
@ -706,7 +714,6 @@ fn create_depth_texture(
device: &wgpu::Device, device: &wgpu::Device,
width: u32, width: u32,
height: u32, height: u32,
// format: wgpu::TextureFormat,
) -> (wgpu::Texture, wgpu::TextureView) { ) -> (wgpu::Texture, wgpu::TextureView) {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width, width,

View file

@ -5,12 +5,15 @@ use ctx::{Renderer, Vertex};
use std::env; use std::env;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use tobj::Mesh;
use tobj::{LoadOptions, Model}; use tobj::{LoadOptions, Model};
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, trace, warn};
use wgpu::rwh::HasWindowHandle; use wgpu::rwh::HasWindowHandle;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::{KeyEvent, WindowEvent}; use winit::event::{KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::monitor::MonitorHandle;
use winit::window::Fullscreen;
use winit::window::Window; use winit::window::Window;
use winit::window::WindowId; use winit::window::WindowId;
@ -42,16 +45,16 @@ pub struct App<'window> {
static CUBE_OBJ: &str = " static CUBE_OBJ: &str = "
# Blender 4.2.3 LTS # Blender 4.2.3 LTS
# www.blender.org # www.blender.org
mtllib cube.mtl mtllib untitled.mtl
o Cube o Cube
v 1.000000 1.000000 -1.000000 v 0.645975 0.645975 -0.645975
v 1.000000 -1.000000 -1.000000 v 0.645975 -0.645975 -0.645975
v 1.000000 1.000000 1.000000 v 0.645975 0.645975 0.645975
v 1.000000 -1.000000 1.000000 v 0.645975 -0.645975 0.645975
v -1.000000 1.000000 -1.000000 v -0.645975 0.645975 -0.645975
v -1.000000 -1.000000 -1.000000 v -0.645975 -0.645975 -0.645975
v -1.000000 1.000000 1.000000 v -0.645975 0.645975 0.645975
v -1.000000 -1.000000 1.000000 v -0.645975 -0.645975 0.645975
vn -0.0000 1.0000 -0.0000 vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000 vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000 vn -1.0000 -0.0000 -0.0000
@ -80,6 +83,7 @@ f 8/8/3 7/9/3 5/10/3 6/11/3
f 6/12/4 2/13/4 4/5/4 8/14/4 f 6/12/4 2/13/4 4/5/4 8/14/4
f 2/13/5 1/1/5 3/4/5 4/5/5 f 2/13/5 1/1/5 3/4/5 4/5/5
f 6/11/6 5/10/6 1/1/6 2/13/6 f 6/11/6 5/10/6 1/1/6 2/13/6
"; ";
impl App<'_> { impl App<'_> {
@ -101,10 +105,22 @@ impl App<'_> {
) { ) {
Ok(obj) => obj, Ok(obj) => obj,
Err(e) => { Err(e) => {
error!("{e}"); error!("Failed to load Pumpkin.obj: {e}");
panic!() // Fallback to CUBE_OBJ
let fallback_obj = CUBE_OBJ.to_string();
tobj::load_obj_buf(
&mut fallback_obj.as_bytes(),
&LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
|_| Ok(Default::default()),
)
.expect("Failed to load fallback CUBE_OBJ")
} }
}; };
let (combined_vertices, combined_indices) = parse_obj(&obj.0); let (combined_vertices, combined_indices) = parse_obj(&obj.0);
wgpu_ctx.add_model(&combined_vertices, &combined_indices); wgpu_ctx.add_model(&combined_vertices, &combined_indices);
@ -119,10 +135,10 @@ impl App<'_> {
); );
info!("Main window created: {:?}", window_id); info!("Main window created: {:?}", window_id);
} }
Err(e) => error!("Failed to create WGPU context: {:?}", e), Err(e) => error!("Failed to create WGPU context: {:}", e),
} }
} }
Err(e) => error!("Failed to create main window: {:?}", e), Err(e) => error!("Failed to create main window: {}", e),
} }
} }
@ -151,6 +167,7 @@ impl App<'_> {
winit::keyboard::KeyCode::Escape => { winit::keyboard::KeyCode::Escape => {
self.spawn_child_window(event_loop); self.spawn_child_window(event_loop);
} }
winit::keyboard::KeyCode::F11 => self.toggle_fullscreen(window_id),
other => error!("Unimplemented keycode: {:?}", other), other => error!("Unimplemented keycode: {:?}", other),
}, },
_ => debug!("Received a keyboard event with no physical key"), _ => debug!("Received a keyboard event with no physical key"),
@ -180,6 +197,23 @@ impl App<'_> {
warn!("No window context for toggling background: {:?}", window_id); warn!("No window context for toggling background: {:?}", window_id);
} }
} }
fn toggle_fullscreen(&mut self, window_id: WindowId) {
if let Some(ctx) = self.windows.get_mut(&window_id) {
let is_fullscreen = ctx.window.fullscreen().is_some();
let fullscreen_mode = if is_fullscreen {
None
} else {
ctx.window
.current_monitor()
.map(|monitor| Fullscreen::Borderless(Some(monitor)))
};
ctx.window.set_fullscreen(fullscreen_mode);
debug!("Fullscreen toggled for window: {:?}", window_id);
} else {
warn!("No window found for fullscreen toggle: {:?}", window_id);
}
}
fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) { fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) {
if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) { if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) {
@ -202,7 +236,7 @@ impl App<'_> {
let mut tmp_path: PathBuf = env::temp_dir(); let mut tmp_path: PathBuf = env::temp_dir();
tmp_path.push("cube.obj"); tmp_path.push("cube.obj");
if let Err(e) = fs::write(&tmp_path, CUBE_OBJ) { if let Err(e) = fs::write(&tmp_path, CUBE_OBJ) {
error!("Failed to write cube OBJ to temp: {:?}", e); error!("Failed to write cube OBJ to temp: {}", e);
} }
let load_options = tobj::LoadOptions { let load_options = tobj::LoadOptions {
@ -217,7 +251,7 @@ impl App<'_> {
wgpu_ctx.add_model(&cube_vertices, &cube_indices); wgpu_ctx.add_model(&cube_vertices, &cube_indices);
} }
Err(e) => { Err(e) => {
error!("Failed to load cube OBJ from temp file: {:?}", e) error!("Failed to load cube OBJ from temp file: {:#}", e)
} }
} }
} }
@ -231,10 +265,10 @@ impl App<'_> {
); );
debug!("Spawned new child window: {:?}", window_id); debug!("Spawned new child window: {:?}", window_id);
} }
Err(e) => error!("Failed to create WGPU context for child window: {:?}", e), Err(e) => error!("Failed to create WGPU context for child window: {}", e),
} }
} }
Err(e) => error!("Failed to create child window: {:?}", e), Err(e) => error!("Failed to create child window: {}", e),
} }
} else { } else {
error!("No main window found. Cannot spawn a child window."); error!("No main window found. Cannot spawn a child window.");
@ -350,6 +384,6 @@ pub fn init_renderer(event_loop: EventLoop<()>) {
event_loop.set_control_flow(ControlFlow::Wait); event_loop.set_control_flow(ControlFlow::Wait);
let mut app = App::default(); let mut app = App::default();
if let Err(e) = event_loop.run_app(&mut app) { if let Err(e) = event_loop.run_app(&mut app) {
error!("Failed to run application: {:?}", e); error!("Failed to run application: {}", e);
} }
} }

View file

@ -33,18 +33,29 @@ async fn main() -> anyhow::Result<()> {
setup(); setup();
let repl_thread = std::thread::spawn(|| { let repl_thread = std::thread::spawn(|| {
let rt = runtime::Builder::new_current_thread() let rt = match runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build() .build() {
.unwrap(); Ok(rt) => rt,
Err(e) => {
error!("A fatal error has occured: {e}");
std::process::exit(1)
},
};
rt.block_on(core::repl::input::handle_repl()) rt.block_on(core::repl::input::handle_repl())
}); });
let event_loop = EventLoop::new().unwrap();
splash::print_splash(); splash::print_splash();
info!("Type 'help' for a list of commands."); info!("Type 'help' for a list of commands.");
core::render::init_renderer(event_loop); match EventLoop::new() {
Ok(event_loop) => {
core::render::init_renderer(event_loop);
}
Err(e) => {
error!("{e}")
}
};
if let Err(_) = repl_thread.join() { if let Err(_) = repl_thread.join() {
eprintln!("REPL thread panicked"); eprintln!("REPL thread panicked");
} }