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::{DialogBuilder, MessageLevel};
79
80 let mbox = move || {
81 DialogBuilder::message()
82 .set_level(MessageLevel::Error)
83 .set_title("Veloren has crashed!")
84 .set_text(dialog_message.replace('<', "[").replace('>', "]"))
87 .alert()
88 .show()
89 .unwrap()
90 };
91
92 #[cfg(target_os = "windows")]
94 {
95 let builder = std::thread::Builder::new().name("shutdown".into());
96 builder
97 .spawn(move || {
98 mbox();
99 })
100 .unwrap()
101 .join()
102 .unwrap();
103 }
104
105 #[cfg(not(target_os = "windows"))]
106 mbox();
107 }
108
109 default_hook(panic_info);
110 }));
111}
112
113enum PotentialPanicCause {
114 GraphicsCardIncompatibleWithRenderingBackend,
115}
116
117fn potential_cause(panic_info: &PanicHookInfo) -> Option<String> {
118 let location = panic_info
119 .location()
120 .map_or("".to_string(), |x| x.file().to_string())
121 .to_lowercase();
122
123 let potential_cause = if location.contains("gfx") || location.contains("wgpu") {
127 Some(PotentialPanicCause::GraphicsCardIncompatibleWithRenderingBackend)
128 } else {
129 None
130 };
131
132 potential_cause.map(potential_cause_to_string)
133}
134
135fn potential_cause_to_string(potential_cause: PotentialPanicCause) -> String {
136 match potential_cause {
137 PotentialPanicCause::GraphicsCardIncompatibleWithRenderingBackend => {
138 "This error occurs when your graphics card is not compatible with the selected \
139 graphics mode. This can be changed in the Airshipper settings window, however it may \
140 be the case that your graphics card is not supported by any graphics mode."
141 .to_string()
142 },
143 }
144}