main/
setup.rs

1use clap::Parser;
2use utils::args::Args;
3
4// Constants
5#[cfg(target_os = "android")]
6const TAG: &str = "CatgirlEngine";
7
8/// Process args for future use
9pub(super) fn process_args() {
10    // Store resources path in separate variable
11    #[cfg(feature = "client")]
12    client::game::store_resources_path(get_args().resources);
13
14    // Uninstall desktop files
15    #[cfg(all(feature = "client", target_os = "linux"))]
16    if get_args().uninstall_desktop_files {
17        trace!("Uninstalling desktop files...");
18        let _ = client::uninstall_desktop_files();
19    }
20
21    // Install desktop files
22    #[cfg(all(feature = "client", target_os = "linux"))]
23    if get_args().install_desktop_files {
24        trace!("Installing desktop files...");
25        let _ = client::install_desktop_files();
26    }
27
28    #[cfg(not(target_family = "wasm"))]
29    if get_args().print_environment_variables {
30        trace!("Printing environment variables...");
31        utils::environment::print_environment_vars();
32    }
33
34    #[cfg(feature = "client")]
35    trace!("Resources Path: {:?}", client::game::get_resources_path());
36}
37
38/// Retrieve parsed out command line arguments
39///
40/// # Panics
41///
42/// This may panic if the args cannot be unwrapped
43#[must_use]
44pub(super) fn get_args() -> Args {
45    if utils::args::get_args().is_some() {
46        utils::args::get_args().unwrap()
47    } else {
48        Args::parse()
49    }
50}
51
52/// Setup the logger for the current platform
53#[cfg(feature = "logging-subscriber")]
54pub(super) fn setup_logger() {
55    if cfg!(target_os = "android") {
56        // Limited Filter: trace,android_activity=debug,winit=debug
57        // Stronger Filter: trace,android_activity=off,winit=off
58
59        #[cfg(target_os = "android")]
60        android_logger::init_once(
61            android_logger::Config::default()
62                .with_max_level(tracing::log::LevelFilter::Trace)
63                .with_tag(TAG)
64                .with_filter(
65                    android_logger::FilterBuilder::new()
66                        .parse("main=trace,catgirl_engine=trace")
67                        .build(),
68                ),
69        );
70    } else if cfg!(target_family = "wasm") {
71        // https://github.com/daboross/fern/issues/134
72        #[cfg(target_family = "wasm")]
73        if let Err(_error) = fern::Dispatch::new()
74            .level(tracing::log::LevelFilter::Off)
75            .level_for("main", tracing::log::LevelFilter::Trace)
76            .level_for("catgirl_engine", tracing::log::LevelFilter::Trace)
77            .level_for("catgirl_engine_client", tracing::log::LevelFilter::Trace)
78            .level_for("catgirl_engine_server", tracing::log::LevelFilter::Trace)
79            .level_for("catgirl_engine_utils", tracing::log::LevelFilter::Trace)
80            .chain(fern::Output::call(console_log::log))
81            .apply()
82        {
83            warn!("Failed to initialize console logger...")
84        }
85    } else {
86        // windows, unix (which includes Linux, BSD, and OSX), or target_os = "macos"
87        let mut builder = pretty_env_logger::formatted_builder();
88        if let Ok(s) = std::env::var("RUST_LOG") {
89            // Set logger according to RUST_LOG environment variable
90            builder.parse_filters(&s);
91        } else {
92            // Failed to find RUST_LOG environment variable
93            builder
94                .default_format()
95                .filter(Some("main"), tracing::log::LevelFilter::Info)
96                .filter(Some("catgirl_engine"), tracing::log::LevelFilter::Info)
97                .filter(
98                    Some("catgirl_engine_client"),
99                    tracing::log::LevelFilter::Info,
100                )
101                .filter(
102                    Some("catgirl_engine_server"),
103                    tracing::log::LevelFilter::Info,
104                )
105                .filter(
106                    Some("catgirl_engine_utils"),
107                    tracing::log::LevelFilter::Info,
108                );
109        }
110
111        builder.try_init().unwrap();
112    }
113}
114
115/// Setup a hook to catch panics for logging before shutdown
116fn set_panic_hook() {
117    std::panic::set_hook(Box::new(|info| {
118        let location_string = if let Some(location) = info.location() {
119            format!(
120                " in file {} at line:column {}:{}",
121                location.file(),
122                location.line(),
123                location.column()
124            )
125        } else {
126            String::new()
127        };
128
129        if let Some(string) = info.payload().downcast_ref::<String>() {
130            error!("Caught panic{location_string}: {string}");
131        } else {
132            error!("Caught panic{location_string}");
133        }
134
135        utils::exit::set_exit();
136    }));
137}
138
139// /// This functions intentionally triggers a panic
140// ///
141// /// # Panics
142// ///
143// /// Always
144// fn trigger_panic() {
145//     let message: &str = "Intentionally triggered a panic for debugging...";
146
147//     // So, I can't pass a String from to_string(), but can pass as a formatted string
148//     panic!("{}", message);
149// }
150
151/// Determines if client or server and starts the engine
152///
153/// # Panics
154///
155/// This may fail to set the ctrl+c handler
156///
157/// # Errors
158///
159/// This may fail to set the ctrl+c handler
160pub(super) fn start() -> Result<(), String> {
161    info!("Starting Game...");
162
163    debug!("Setting panic hook...");
164    set_panic_hook();
165
166    // Allows handling properly shutting down with SIGINT
167    #[cfg(any(target_family = "unix", target_family = "windows"))]
168    {
169        debug!("Setting SIGINT hook...");
170        ctrlc::set_handler(move || {
171            debug!("SIGINT (Ctrl+C) Was Called! Stopping...");
172            utils::exit::set_exit();
173
174            #[cfg(feature = "client")]
175            if !get_args().server {
176                #[cfg(not(target_family = "wasm"))]
177                let _ = client::game::advance_event_loop();
178            }
179        })
180        .expect("Could not create Interrupt Handler (e.g. Ctrl+C)...");
181    }
182
183    debug!("Starting main loop...");
184
185    #[cfg(feature = "server")]
186    if cfg!(not(feature = "client")) || get_args().server {
187        // Server exists, client may exist
188        return server::game::game_loop();
189    }
190
191    // Client exists, server may exist
192    #[cfg(feature = "client")]
193    return client::game::game_loop();
194
195    // Server doesn't exist, client doesn't exist
196    #[cfg(not(feature = "client"))]
197    Err("Neither the client nor server features were configured at build time...".to_string())
198}