use std::{panic, panic::PanicInfo, path::PathBuf};
use tracing::error;
pub fn set_panic_hook(log_filename: String, logs_dir: PathBuf) {
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
let panic_info_payload = panic_info.payload();
let payload_string = panic_info_payload.downcast_ref::<String>();
let reason = match payload_string {
Some(s) => s,
None => {
let payload_str = panic_info_payload.downcast_ref::<&str>();
match payload_str {
Some(st) => st,
None => "Payload is not a string",
}
},
};
let potential_cause = potential_cause(panic_info);
let mut dialog_message = format!(
"A critical error has occurred and Voxygen has been forced to terminate in an unusual \
manner. Details about the error can be found below.\n\nPanic reason: {}\n\n",
reason
);
if let Some(potential_cause) = potential_cause {
dialog_message.push_str(format!("Potential causes: {}\n\n", potential_cause).as_str())
} else {
dialog_message.push_str(
format!("> What should I do?\n\
\n\
We need your help to fix this! You can help by contacting us and \
reporting this problem. To do this, open an issue on the Veloren \
issue tracker:\n\
\n\
https://www.gitlab.com/veloren/veloren/issues/new\n\
\n\
If you're on the Veloren community Discord server, we'd be \
grateful if you could also post a message in the #support channel.
\n\
> What should I include?\n\
\n\
The error information below will be useful in finding and fixing \
the problem. Please include as much information about your setup \
and the events that led up to the panic as possible.
\n\
Voxygen has logged information about the problem (including this \
message) to the file {}. Please include the contents of this \
file in your bug report.
\n\n", logs_dir.join(&log_filename).display())
.as_str(),
);
}
dialog_message.push_str(
format!(
"> Error information\n\nThe information below is intended for developers and \
testers.\n\nPanicInfo: {} \nGame version: {} [{}]",
panic_info,
*common::util::GIT_HASH,
*common::util::GIT_DATE
)
.as_str(),
);
error!(
"VOXYGEN HAS PANICKED\n\n{}\n\nBacktrace:\n{:?}",
dialog_message,
backtrace::Backtrace::new(),
);
#[cfg(feature = "native-dialog")]
{
use native_dialog::{MessageDialog, MessageType};
let mbox = move || {
MessageDialog::new()
.set_title("Veloren has crashed!")
.set_text(&dialog_message.replace('<', "[").replace('>', "]"))
.set_type(MessageType::Error)
.show_alert()
.unwrap()
};
#[cfg(target_os = "windows")]
{
let builder = std::thread::Builder::new().name("shutdown".into());
builder
.spawn(move || {
mbox();
})
.unwrap()
.join()
.unwrap();
}
#[cfg(not(target_os = "windows"))]
mbox();
}
default_hook(panic_info);
}));
}
enum PotentialPanicCause {
GraphicsCardIncompatibleWithRenderingBackend,
}
fn potential_cause(panic_info: &PanicInfo) -> Option<String> {
let location = panic_info
.location()
.map_or("".to_string(), |x| x.file().to_string())
.to_lowercase();
let potential_cause = if location.contains("gfx") || location.contains("wgpu") {
Some(PotentialPanicCause::GraphicsCardIncompatibleWithRenderingBackend)
} else {
None
};
potential_cause.map(potential_cause_to_string)
}
fn potential_cause_to_string(potential_cause: PotentialPanicCause) -> String {
match potential_cause {
PotentialPanicCause::GraphicsCardIncompatibleWithRenderingBackend => {
"This error occurs when your graphics card is not compatible with the selected \
graphics mode. This can be changed in the Airshipper settings window, however it may \
be the case that your graphics card is not supported by any graphics mode."
.to_string()
},
}
}