veloren_voxygen/
panic_handler.rs1use std::{panic, panic::PanicHookInfo, path::PathBuf};
2use tracing::error;
3
4pub fn set_panic_hook(log_filename: String, logs_dir: PathBuf) {
5 let default_hook = panic::take_hook();
7 panic::set_hook(Box::new(move |panic_info| {
8 let panic_info_payload = panic_info.payload();
9 let payload_string = panic_info_payload.downcast_ref::<String>();
10 let reason = match payload_string {
11 Some(s) => s,
12 None => {
13 let payload_str = panic_info_payload.downcast_ref::<&str>();
14 match payload_str {
15 Some(st) => st,
16 None => "Payload is not a string",
17 }
18 },
19 };
20 let potential_cause = potential_cause(panic_info);
21
22 let mut dialog_message = format!(
23 "A critical error has occurred and Voxygen has been forced to terminate in an unusual \
24 manner. Details about the error can be found below.\n\nPanic reason: {}\n\n",
25 reason
26 );
27
28 if let Some(potential_cause) = potential_cause {
29 dialog_message.push_str(format!("Potential causes: {}\n\n", potential_cause).as_str())
32 } else {
33 dialog_message.push_str(
34 format!("> What should I do?\n\
35 \n\
36 We need your help to fix this! You can help by contacting us and \
37 reporting this problem. To do this, open an issue on the Veloren \
38 issue tracker:\n\
39 \n\
40 https://www.gitlab.com/veloren/veloren/issues/new\n\
41 \n\
42 If you're on the Veloren community Discord server, we'd be \
43 grateful if you could also post a message in the #support channel.
44 \n\
45 > What should I include?\n\
46 \n\
47 The error information below will be useful in finding and fixing \
48 the problem. Please include as much information about your setup \
49 and the events that led up to the panic as possible.
50 \n\
51 Voxygen has logged information about the problem (including this \
52 message) to the file {}. Please include the contents of this \
53 file in your bug report.
54 \n\n", logs_dir.join(&log_filename).display())
55 .as_str(),
56 );
57 }
58
59 dialog_message.push_str(
60 format!(
61 "> Error information\n\nThe information below is intended for developers and \
62 testers.\n\nPanicHookInfo: {} \nGame version: {} [{}]",
63 panic_info,
64 *common::util::GIT_HASH,
65 *common::util::GIT_DATE
66 )
67 .as_str(),
68 );
69
70 error!(
71 "VOXYGEN HAS PANICKED\n\n{}\n\nBacktrace:\n{:?}",
72 dialog_message,
73 backtrace::Backtrace::new(),
74 );
75
76 #[cfg(feature = "native-dialog")]
77 {
78 use native_dialog::{MessageDialog, MessageType};
79
80 let mbox = move || {
81 MessageDialog::new()
82 .set_title("Veloren has crashed!")
83 .set_text(&dialog_message.replace('<', "[").replace('>', "]"))
86 .set_type(MessageType::Error)
87 .show_alert()
88 .unwrap()
89 };
90
91 #[cfg(target_os = "windows")]
93 {
94 let builder = std::thread::Builder::new().name("shutdown".into());
95 builder
96 .spawn(move || {
97 mbox();
98 })
99 .unwrap()
100 .join()
101 .unwrap();
102 }
103
104 #[cfg(not(target_os = "windows"))]
105 mbox();
106 }
107
108 default_hook(panic_info);
109 }));
110}
111
112enum PotentialPanicCause {
113 GraphicsCardIncompatibleWithRenderingBackend,
114}
115
116fn potential_cause(panic_info: &PanicHookInfo) -> Option<String> {
117 let location = panic_info
118 .location()
119 .map_or("".to_string(), |x| x.file().to_string())
120 .to_lowercase();
121
122 let potential_cause = if location.contains("gfx") || location.contains("wgpu") {
126 Some(PotentialPanicCause::GraphicsCardIncompatibleWithRenderingBackend)
127 } else {
128 None
129 };
130
131 potential_cause.map(potential_cause_to_string)
132}
133
134fn potential_cause_to_string(potential_cause: PotentialPanicCause) -> String {
135 match potential_cause {
136 PotentialPanicCause::GraphicsCardIncompatibleWithRenderingBackend => {
137 "This error occurs when your graphics card is not compatible with the selected \
138 graphics mode. This can be changed in the Airshipper settings window, however it may \
139 be the case that your graphics card is not supported by any graphics mode."
140 .to_string()
141 },
142 }
143}