Skip to content

Commit e8afb49

Browse files
Refactor + Check for env variables (SlimeVR#1261)
1 parent bc487f8 commit e8afb49

File tree

3 files changed

+147
-83
lines changed

3 files changed

+147
-83
lines changed

Cargo.lock

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gui/src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ rfd = { version = "0.15", features = ["gtk3"], default-features = false }
5353
dirs-next = "2.0.0"
5454
discord-sdk = "0.3.6"
5555
tokio = { version = "1.37.0", features = ["time"] }
56+
itertools = "0.13.0"
5657

5758
[target.'cfg(windows)'.dependencies]
5859
win32job = "1"

gui/src-tauri/src/main.rs

Lines changed: 135 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![cfg_attr(all(not(debug_assertions), windows), windows_subsystem = "windows")]
2+
use std::env;
23
use std::panic;
34
use std::sync::atomic::AtomicBool;
45
use std::sync::atomic::Ordering;
@@ -68,88 +69,125 @@ fn main() -> Result<()> {
6869
let tauri_context = tauri::generate_context!();
6970

7071
// Set up loggers and global handlers
71-
let _logger = {
72-
use flexi_logger::{
73-
Age, Cleanup, Criterion, Duplicate, FileSpec, Logger, Naming, WriteMode,
74-
};
75-
use tauri::Error;
76-
77-
// Based on https://docs.rs/tauri/2.0.0-alpha.10/src/tauri/path/desktop.rs.html#238-256
78-
#[cfg(target_os = "macos")]
79-
let path = dirs_next::home_dir().ok_or(Error::UnknownPath).map(|dir| {
80-
dir.join("Library/Logs")
81-
.join(&tauri_context.config().identifier)
82-
});
83-
84-
#[cfg(not(target_os = "macos"))]
85-
let path = dirs_next::data_dir()
86-
.ok_or(Error::UnknownPath)
87-
.map(|dir| dir.join(&tauri_context.config().identifier).join("logs"));
88-
89-
Logger::try_with_env_or_str("info")?
90-
.log_to_file(
91-
FileSpec::default().directory(path.expect("We need a log dir")),
92-
)
93-
.format_for_files(|w, now, record| {
94-
util::logger_format(w, now, record, false)
95-
})
96-
.format_for_stderr(|w, now, record| {
97-
util::logger_format(w, now, record, true)
98-
})
99-
.rotate(
100-
Criterion::Age(Age::Day),
101-
Naming::Timestamps,
102-
Cleanup::KeepLogFiles(2),
103-
)
104-
.duplicate_to_stderr(Duplicate::All)
105-
.write_mode(WriteMode::BufferAndFlush)
106-
.start()?
107-
};
72+
let _logger = setup_logger(&tauri_context);
10873

10974
// Ensure child processes die when spawned on windows
11075
// and then check for WebView2's existence
11176
#[cfg(windows)]
112-
{
113-
use crate::util::webview2_exists;
114-
use win32job::{ExtendedLimitInfo, Job};
115-
116-
let mut info = ExtendedLimitInfo::new();
117-
info.limit_kill_on_job_close();
118-
let job = Job::create_with_limit_info(&mut info).expect("Failed to create Job");
119-
job.assign_current_process()
120-
.expect("Failed to assign current process to Job");
121-
122-
// We don't do anything with the job anymore, but we shouldn't drop it because that would
123-
// terminate our process tree. So we intentionally leak it instead.
124-
std::mem::forget(job);
125-
126-
if !webview2_exists() {
127-
// This makes a dialog appear which let's you press Ok or Cancel
128-
// If you press Ok it will open the SlimeVR installer documentation
129-
use rfd::{
130-
MessageButtons, MessageDialog, MessageDialogResult, MessageLevel,
131-
};
77+
setup_webview2()?;
13278

133-
let confirm = MessageDialog::new()
134-
.set_title("SlimeVR")
135-
.set_description("Couldn't find WebView2 installed. You can install it with the SlimeVR installer")
136-
.set_buttons(MessageButtons::OkCancel)
137-
.set_level(MessageLevel::Error)
138-
.show();
139-
if confirm == MessageDialogResult::Ok {
140-
open::that("https://docs.slimevr.dev/server-setup/installing-and-connecting.html#install-the-latest-slimevr-installer").unwrap();
141-
}
142-
return Ok(());
143-
}
144-
}
79+
// Check for environment variables that can affect the server, and if so, warn in log and GUI
80+
check_environment_variables();
14581

14682
// Spawn server process
14783
let exit_flag = Arc::new(AtomicBool::new(false));
14884
let backend = Arc::new(Mutex::new(Option::<CommandChild>::None));
149-
let backend_termination = backend.clone();
150-
let run_path = get_launch_path(cli);
15185

152-
let server_info = if let Some(p) = run_path {
86+
let server_info = execute_server(cli)?;
87+
let build_result = setup_tauri(
88+
tauri_context,
89+
server_info,
90+
exit_flag.clone(),
91+
backend.clone(),
92+
);
93+
94+
tauri_build_result(build_result, exit_flag, backend);
95+
96+
Ok(())
97+
}
98+
99+
fn setup_logger(context: &tauri::Context) -> Result<flexi_logger::LoggerHandle> {
100+
use flexi_logger::{
101+
Age, Cleanup, Criterion, Duplicate, FileSpec, Logger, Naming, WriteMode,
102+
};
103+
use tauri::Error;
104+
105+
// Based on https://docs.rs/tauri/2.0.0-alpha.10/src/tauri/path/desktop.rs.html#238-256
106+
#[cfg(target_os = "macos")]
107+
let path = dirs_next::home_dir()
108+
.ok_or(Error::UnknownPath)
109+
.map(|dir| dir.join("Library/Logs").join(&context.config().identifier));
110+
111+
#[cfg(not(target_os = "macos"))]
112+
let path = dirs_next::data_dir()
113+
.ok_or(Error::UnknownPath)
114+
.map(|dir| dir.join(&context.config().identifier).join("logs"));
115+
116+
Ok(Logger::try_with_env_or_str("info")?
117+
.log_to_file(FileSpec::default().directory(path.expect("We need a log dir")))
118+
.format_for_files(|w, now, record| util::logger_format(w, now, record, false))
119+
.format_for_stderr(|w, now, record| util::logger_format(w, now, record, true))
120+
.rotate(
121+
Criterion::Age(Age::Day),
122+
Naming::Timestamps,
123+
Cleanup::KeepLogFiles(2),
124+
)
125+
.duplicate_to_stderr(Duplicate::All)
126+
.write_mode(WriteMode::BufferAndFlush)
127+
.start()?)
128+
}
129+
130+
#[cfg(windows)]
131+
fn setup_webview2() -> Result<()> {
132+
use crate::util::webview2_exists;
133+
use win32job::{ExtendedLimitInfo, Job};
134+
135+
let mut info = ExtendedLimitInfo::new();
136+
info.limit_kill_on_job_close();
137+
let job = Job::create_with_limit_info(&mut info).expect("Failed to create Job");
138+
job.assign_current_process()
139+
.expect("Failed to assign current process to Job");
140+
141+
// We don't do anything with the job anymore, but we shouldn't drop it because that would
142+
// terminate our process tree. So we intentionally leak it instead.
143+
std::mem::forget(job);
144+
145+
if !webview2_exists() {
146+
// This makes a dialog appear which let's you press Ok or Cancel
147+
// If you press Ok it will open the SlimeVR installer documentation
148+
use rfd::{MessageButtons, MessageDialog, MessageDialogResult, MessageLevel};
149+
150+
let confirm = MessageDialog::new()
151+
.set_title("SlimeVR")
152+
.set_description("Couldn't find WebView2 installed. You can install it with the SlimeVR installer")
153+
.set_buttons(MessageButtons::OkCancel)
154+
.set_level(MessageLevel::Error)
155+
.show();
156+
if confirm == MessageDialogResult::Ok {
157+
open::that("https://docs.slimevr.dev/server-setup/installing-and-connecting.html#install-the-latest-slimevr-installer").unwrap();
158+
}
159+
}
160+
Ok(())
161+
}
162+
163+
fn check_environment_variables() {
164+
use itertools::Itertools;
165+
const ENVS_TO_CHECK: &[&str] = &["_JAVA_OPTIONS", "JAVA_TOOL_OPTIONS"];
166+
let checked_envs = ENVS_TO_CHECK
167+
.into_iter()
168+
.filter_map(|e| {
169+
let Ok(data) = env::var(e) else {
170+
return None;
171+
};
172+
log::warn!("{e} is set to: {data}");
173+
Some(e)
174+
})
175+
.join(", ");
176+
177+
if !checked_envs.is_empty() {
178+
rfd::MessageDialog::new()
179+
.set_title("SlimeVR")
180+
.set_description(format!("You have environment variables {} set, which may cause the SlimeVR Server to fail to launch properly.", checked_envs))
181+
.set_level(rfd::MessageLevel::Warning)
182+
.show();
183+
}
184+
}
185+
186+
fn execute_server(
187+
cli: Cli,
188+
) -> Result<Option<(std::ffi::OsString, std::path::PathBuf)>> {
189+
use const_format::formatcp;
190+
if let Some(p) = get_launch_path(cli) {
153191
log::info!("Server found on path: {}", p.to_str().unwrap());
154192

155193
// Check if any Java already installed is compatible
@@ -159,19 +197,29 @@ fn main() -> Result<()> {
159197
.then(|| jre.into_os_string())
160198
.or_else(|| valid_java_paths().first().map(|x| x.0.to_owned()));
161199
let Some(java_bin) = java_bin else {
162-
show_error(&format!("Couldn't find a compatible Java version, please download Java {} or higher", MINIMUM_JAVA_VERSION));
163-
return Ok(());
200+
show_error(formatcp!(
201+
"Couldn't find a compatible Java version, please download Java {} or higher",
202+
MINIMUM_JAVA_VERSION
203+
));
204+
return Ok(None);
164205
};
165206

166207
log::info!("Using Java binary: {:?}", java_bin);
167-
Some((java_bin, p))
208+
Ok(Some((java_bin, p)))
168209
} else {
169210
log::warn!("No server found. We will not start the server.");
170-
None
171-
};
211+
Ok(None)
212+
}
213+
}
172214

215+
fn setup_tauri(
216+
context: tauri::Context,
217+
server_info: Option<(std::ffi::OsString, std::path::PathBuf)>,
218+
exit_flag: Arc<AtomicBool>,
219+
backend: Arc<Mutex<Option<CommandChild>>>,
220+
) -> Result<tauri::App, tauri::Error> {
173221
let exit_flag_terminated = exit_flag.clone();
174-
let build_result = tauri::Builder::default()
222+
tauri::Builder::default()
175223
.plugin(tauri_plugin_dialog::init())
176224
.plugin(tauri_plugin_fs::init())
177225
.plugin(tauri_plugin_os::init())
@@ -281,7 +329,14 @@ fn main() -> Result<()> {
281329
// WindowEvent::Resized(_) => std::thread::sleep(std::time::Duration::from_nanos(1)),
282330
_ => (),
283331
})
284-
.build(tauri_context);
332+
.build(context)
333+
}
334+
335+
fn tauri_build_result(
336+
build_result: Result<tauri::App, tauri::Error>,
337+
exit_flag: Arc<AtomicBool>,
338+
backend: Arc<Mutex<Option<CommandChild>>>,
339+
) {
285340
match build_result {
286341
Ok(app) => {
287342
app.run(move |app_handle, event| match event {
@@ -295,7 +350,7 @@ fn main() -> Result<()> {
295350
Err(e) => log::error!("failed to save window state: {}", e),
296351
}
297352

298-
let mut lock = backend_termination.lock().unwrap();
353+
let mut lock = backend.lock().unwrap();
299354
let Some(ref mut child) = *lock else { return };
300355
let write_result = child.write(b"exit\n");
301356
match write_result {
@@ -339,6 +394,4 @@ fn main() -> Result<()> {
339394
show_error(&error.to_string());
340395
}
341396
}
342-
343-
Ok(())
344397
}

0 commit comments

Comments
 (0)