flatland3-gfx 0.2.35

Flatland3 Macroquad + egui graphical play client
//! Append-only session log for gfx crashes (see `~/.config/flatland/gfx-session.log`).

use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use std::sync::Mutex;

static LOG_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);

fn log_path() -> PathBuf {
    if let Ok(guard) = LOG_PATH.lock() {
        if let Some(p) = guard.as_ref() {
            return p.clone();
        }
    }
    let p = dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("."))
        .join("flatland")
        .join("gfx-session.log");
    if let Ok(mut guard) = LOG_PATH.lock() {
        *guard = Some(p.clone());
    }
    p
}

pub fn install_panic_hook() {
    let default = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |info| {
        let msg = format!("PANIC: {info}");
        eprintln!("{msg}");
        session_log(&msg);
        if let Some(loc) = info.location() {
            session_log(&format!("  at {}:{}:{}", loc.file(), loc.line(), loc.column()));
        }
        default(info);
    }));
}

pub fn session_log(line: &str) {
    let path = log_path();
    if let Some(parent) = path.parent() {
        let _ = std::fs::create_dir_all(parent);
    }
    let ts = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_millis())
        .unwrap_or(0);
    if let Ok(mut f) = OpenOptions::new().create(true).append(true).open(&path) {
        let _ = writeln!(f, "[{ts}] {line}");
    }
}