Skip to content

Commit da7cf11

Browse files
committed
feat: ✨ Add server_mock
1 parent 452452e commit da7cf11

File tree

18 files changed

+550
-226
lines changed

18 files changed

+550
-226
lines changed

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

alvr/dashboard/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ rust-version.workspace = true
66
authors.workspace = true
77
license.workspace = true
88

9+
[features]
10+
steamvr-server = []
11+
# monado-server = [] # todo
12+
mock-server = []
13+
default = ["steamvr-server"]
14+
915
[dependencies]
1016
alvr_adb.workspace = true
1117
alvr_common.workspace = true

alvr/dashboard/src/dashboard/components/devices.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ impl DevicesTab {
7575

7676
#[cfg(not(target_arch = "wasm32"))]
7777
ui.with_layout(Layout::right_to_left(eframe::emath::Align::Center), |ui| {
78-
if ui.button("Launch SteamVR").clicked() {
79-
crate::steamvr_launcher::LAUNCHER.lock().launch_steamvr();
78+
if ui.button("Launch server").clicked() {
79+
crate::server_launcher::LAUNCHER.lock().launch_server();
8080
}
8181
});
8282
})

alvr/dashboard/src/dashboard/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl Dashboard {
103103
let server_restarting = Arc::clone(&self.server_restarting);
104104
let condvar = Arc::clone(&self.server_restarting_condvar);
105105
move || {
106-
crate::steamvr_launcher::LAUNCHER.lock().restart_steamvr();
106+
crate::server_launcher::LAUNCHER.lock().restart_server();
107107

108108
*server_restarting.lock() = false;
109109
condvar.notify_one();
@@ -170,7 +170,7 @@ impl eframe::App for Dashboard {
170170
// todo: find a way to center both vertically and horizontally
171171
ui.vertical_centered(|ui| {
172172
ui.add_space(100.0);
173-
ui.heading(RichText::new("SteamVR is restarting").size(30.0));
173+
ui.heading(RichText::new("The server is restarting").size(30.0));
174174
});
175175
});
176176

@@ -231,16 +231,16 @@ impl eframe::App for Dashboard {
231231
ui.add_space(5.0);
232232

233233
if connected_to_server {
234-
if ui.button("Restart SteamVR").clicked() {
234+
if ui.button("Restart server").clicked() {
235235
self.restart_steamvr(&mut requests);
236236
}
237-
} else if ui.button("Launch SteamVR").clicked() {
238-
crate::steamvr_launcher::LAUNCHER.lock().launch_steamvr();
237+
} else if ui.button("Launch server").clicked() {
238+
crate::server_launcher::LAUNCHER.lock().launch_server();
239239
}
240240

241241
ui.horizontal(|ui| {
242242
ui.add_space(5.0);
243-
ui.label(RichText::new("SteamVR:").size(13.0));
243+
ui.label(RichText::new("Server:").size(13.0));
244244
ui.add_space(-10.0);
245245
if connected_to_server {
246246
ui.label(
@@ -307,9 +307,9 @@ impl eframe::App for Dashboard {
307307
let shutdown_alvr = || {
308308
self.data_sources.request(ServerRequest::ShutdownSteamvr);
309309

310-
crate::steamvr_launcher::LAUNCHER
310+
crate::server_launcher::LAUNCHER
311311
.lock()
312-
.ensure_steamvr_shutdown();
312+
.ensure_server_shutdown();
313313
};
314314

315315
if let Some(popup) = &self.new_version_popup
@@ -330,8 +330,8 @@ impl eframe::App for Dashboard {
330330
&& self.session.as_ref().is_some_and(|s| {
331331
s.to_settings()
332332
.extra
333-
.steamvr_launcher
334-
.open_close_steamvr_with_dashboard
333+
.server_launcher
334+
.open_close_server_with_dashboard
335335
})
336336
{
337337
shutdown_alvr();

alvr/dashboard/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod linux_checks;
1313
#[cfg(not(target_arch = "wasm32"))]
1414
mod logging_backend;
1515
#[cfg(not(target_arch = "wasm32"))]
16-
mod steamvr_launcher;
16+
mod server_launcher;
1717

1818
#[cfg(not(target_arch = "wasm32"))]
1919
use data_sources::DataSources;
@@ -65,10 +65,10 @@ fn main() {
6565
if data_sources::get_read_only_local_session()
6666
.settings()
6767
.extra
68-
.steamvr_launcher
69-
.open_close_steamvr_with_dashboard
68+
.server_launcher
69+
.open_close_server_with_dashboard
7070
{
71-
steamvr_launcher::LAUNCHER.lock().launch_steamvr()
71+
server_launcher::LAUNCHER.lock().launch_server()
7272
}
7373

7474
let ico = IconDir::read(Cursor::new(include_bytes!("../resources/dashboard.ico"))).unwrap();
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#[cfg(target_os = "linux")]
2+
mod linux_steamvr;
3+
#[cfg(windows)]
4+
mod windows_steamvr;
5+
6+
use crate::data_sources;
7+
use alvr_adb::commands as adb;
8+
use alvr_common::{
9+
anyhow::{Context, Result},
10+
debug,
11+
glam::bool,
12+
parking_lot::Mutex,
13+
warn,
14+
};
15+
use alvr_filesystem as afs;
16+
use serde_json::{self, json};
17+
use std::{
18+
ffi::OsStr,
19+
fs,
20+
marker::PhantomData,
21+
process::Command,
22+
slice, thread,
23+
time::{Duration, Instant},
24+
};
25+
use sysinfo::{ProcessesToUpdate, System};
26+
27+
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(10);
28+
const DRIVER_KEY: &str = "driver_alvr_server";
29+
const BLOCKED_KEY: &str = "blocked_by_safe_mode";
30+
31+
pub fn is_server_running() -> bool {
32+
#[cfg(feature = "steamvr-server")]
33+
let process_name = afs::exec_fname("vrserver");
34+
#[cfg(feature = "mock-server")]
35+
let process_name = afs::mock_server_fname();
36+
37+
System::new_all()
38+
.processes_by_name(OsStr::new(&process_name))
39+
.count()
40+
!= 0
41+
}
42+
43+
pub fn maybe_kill_server() {
44+
let mut system = System::new_all();
45+
46+
#[cfg(feature = "steamvr-server")]
47+
let process_names = [afs::exec_fname("vrmonitor"), afs::exec_fname("vrserver")];
48+
#[cfg(feature = "mock-server")]
49+
let process_names = [afs::mock_server_fname()];
50+
51+
#[cfg_attr(target_os = "macos", expect(unused_variables))]
52+
for process_name in process_names {
53+
system.refresh_processes(ProcessesToUpdate::All, true);
54+
55+
for process in system.processes_by_name(OsStr::new(&process_name)) {
56+
debug!("Killing {}", process_name);
57+
58+
#[cfg(target_os = "linux")]
59+
linux_steamvr::terminate_process(process);
60+
#[cfg(windows)]
61+
windows_steamvr::kill_process(process.pid().as_u32());
62+
63+
thread::sleep(Duration::from_secs(1));
64+
}
65+
}
66+
}
67+
68+
fn unblock_alvr_driver() -> Result<()> {
69+
if !cfg!(target_os = "linux") {
70+
return Ok(());
71+
}
72+
73+
let path = alvr_server_io::steamvr_settings_file_path()?;
74+
let text = fs::read_to_string(&path).with_context(|| format!("Failed to read {path:?}"))?;
75+
let new_text = unblock_alvr_driver_within_vrsettings(text.as_str())
76+
.with_context(|| "Failed to rewrite .vrsettings.")?;
77+
fs::write(&path, new_text)
78+
.with_context(|| "Failed to write .vrsettings back after changing it.")?;
79+
Ok(())
80+
}
81+
82+
// Reads and writes back steamvr.vrsettings in order to
83+
// ensure the ALVR driver is not blocked (safe mode).
84+
fn unblock_alvr_driver_within_vrsettings(text: &str) -> Result<String> {
85+
let mut settings = serde_json::from_str::<serde_json::Value>(text)?;
86+
let values = settings
87+
.as_object_mut()
88+
.with_context(|| "Failed to parse .vrsettings.")?;
89+
let blocked = values
90+
.get(DRIVER_KEY)
91+
.and_then(|driver| driver.get(BLOCKED_KEY))
92+
.and_then(|blocked| blocked.as_bool())
93+
.unwrap_or(false);
94+
95+
if blocked {
96+
debug!("Unblocking ALVR driver in SteamVR.");
97+
if !values.contains_key(DRIVER_KEY) {
98+
values.insert(DRIVER_KEY.into(), json!({}));
99+
}
100+
let driver = settings[DRIVER_KEY]
101+
.as_object_mut()
102+
.with_context(|| "Did not find ALVR key in settings.")?;
103+
driver.insert(BLOCKED_KEY.into(), json!(false)); // overwrites if present
104+
} else {
105+
debug!("ALVR is not blocked in SteamVR.");
106+
}
107+
108+
Ok(serde_json::to_string_pretty(&settings)?)
109+
}
110+
111+
pub struct Launcher {
112+
_phantom: PhantomData<()>,
113+
}
114+
115+
impl Launcher {
116+
pub fn launch_server(&self) {
117+
let filesystem_layout = crate::get_filesystem_layout();
118+
119+
// The ADB server might be left running because of a unclean termination of SteamVR
120+
// Note that this will also kill a system wide ADB server not started by ALVR
121+
let wired_enabled = data_sources::get_read_only_local_session()
122+
.session()
123+
.client_connections
124+
.contains_key(alvr_sockets::WIRED_CLIENT_HOSTNAME);
125+
if wired_enabled && let Some(path) = adb::get_adb_path(&filesystem_layout) {
126+
adb::kill_server(&path).ok();
127+
}
128+
129+
if cfg!(feature = "steamvr-server") {
130+
#[cfg(target_os = "linux")]
131+
linux_steamvr::linux_hardware_checks();
132+
133+
let alvr_driver_dir = &filesystem_layout.openvr_driver_root_dir;
134+
135+
// Make sure to unregister any other ALVR driver because it would cause a socket conflict
136+
let other_alvr_dirs = alvr_server_io::get_registered_drivers()
137+
.unwrap_or_default()
138+
.into_iter()
139+
.filter(|path| {
140+
path.to_string_lossy().to_lowercase().contains("alvr")
141+
&& path != alvr_driver_dir
142+
})
143+
.collect::<Vec<_>>();
144+
alvr_server_io::driver_registration(&other_alvr_dirs, false).ok();
145+
146+
alvr_server_io::driver_registration(slice::from_ref(alvr_driver_dir), true).ok();
147+
148+
if let Err(err) = unblock_alvr_driver() {
149+
warn!("Failed to unblock ALVR driver: {:?}", err);
150+
}
151+
152+
#[cfg(target_os = "linux")]
153+
{
154+
let vrcompositor_wrap_result = linux_steamvr::maybe_wrap_vrcompositor_launcher();
155+
alvr_common::show_err(linux_steamvr::maybe_wrap_vrcompositor_launcher());
156+
if vrcompositor_wrap_result.is_err() {
157+
return;
158+
}
159+
}
160+
}
161+
162+
if !is_server_running() {
163+
debug!("Server is dead. Launching...");
164+
165+
if cfg!(feature = "steamvr-server") {
166+
#[cfg(windows)]
167+
windows_steamvr::start_steamvr();
168+
169+
#[cfg(target_os = "linux")]
170+
linux_steamvr::start_steamvr();
171+
} else if cfg!(feature = "mock-server") {
172+
Command::new(filesystem_layout.mock_server_exe())
173+
.spawn()
174+
.ok();
175+
}
176+
}
177+
}
178+
179+
pub fn ensure_server_shutdown(&self) {
180+
debug!("Waiting for server to shutdown...");
181+
let start_time = Instant::now();
182+
while start_time.elapsed() < SHUTDOWN_TIMEOUT && is_server_running() {
183+
thread::sleep(Duration::from_millis(500));
184+
}
185+
186+
maybe_kill_server();
187+
}
188+
189+
pub fn restart_server(&self) {
190+
self.ensure_server_shutdown();
191+
self.launch_server();
192+
}
193+
}
194+
195+
// Singleton with exclusive access
196+
pub static LAUNCHER: Mutex<Launcher> = Mutex::new(Launcher {
197+
_phantom: PhantomData,
198+
});

0 commit comments

Comments
 (0)