Files
server/src/core/client_state.rs
2025-05-14 19:58:00 -07:00

120 lines
3.2 KiB
Rust

use super::client::{Client, get_env};
use crate::nodes::{Node, root::ClientState, spatial::Spatial};
use glam::Mat4;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::{
path::{Path, PathBuf},
process::Command,
sync::Arc,
};
lazy_static::lazy_static! {
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientStateParsed>>> = Default::default();
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LaunchInfo {
pub cmdline: Vec<String>,
pub cwd: PathBuf,
pub env: FxHashMap<String, String>,
}
impl LaunchInfo {
fn from_client(client: &Client) -> Option<Self> {
Some(LaunchInfo {
cmdline: client.get_cmdline()?,
cwd: client.get_cwd()?,
env: get_env(client.pid?).ok()?,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ClientStateParsed {
pub launch_info: Option<LaunchInfo>,
pub data: Vec<u8>,
pub root: Mat4,
pub spatial_anchors: FxHashMap<String, Mat4>,
}
impl ClientStateParsed {
pub fn from_deserialized(client: &Client, state: ClientState) -> Self {
ClientStateParsed {
launch_info: LaunchInfo::from_client(client),
data: state.data.unwrap_or_default(),
root: Self::spatial_transform(client, state.root).unwrap_or_default(),
spatial_anchors: state
.spatial_anchors
.into_iter()
.filter_map(|(k, v)| Some((k, Self::spatial_transform(client, v)?)))
.collect(),
}
}
fn spatial_transform(client: &Client, id: u64) -> Option<Mat4> {
let node = client.scenegraph.get_node(id)?;
let spatial = node.get_aspect::<Spatial>().ok()?;
Some(spatial.global_transform())
}
pub fn token(self) -> String {
let token = nanoid::nanoid!();
CLIENT_STATES.lock().insert(token.clone(), Arc::new(self));
token
}
pub fn from_file(file: &Path) -> Option<Self> {
let file_string = std::fs::read_to_string(file).ok()?;
toml::from_str(&file_string).ok()
}
pub fn to_file(&self, directory: &Path) {
let app_name = self
.launch_info
.as_ref()
.map(|l| l.cmdline.first().unwrap().split('/').next_back().unwrap())
.unwrap_or("unknown");
let state_file_path = directory
.join(format!("{app_name}-{}", nanoid::nanoid!()))
.with_extension("toml");
std::fs::write(state_file_path, toml::to_string(&self).unwrap()).unwrap();
}
pub fn apply_to(&self, client: &Arc<Client>) -> ClientState {
if let Some(root) = client.root.get() {
root.set_transform(self.root)
}
ClientState {
data: Some(self.data.clone()),
root: 0,
spatial_anchors: self
.spatial_anchors
.iter()
.map(|(k, v)| {
let node = Node::generate(client, true).add_to_scenegraph().unwrap();
Spatial::add_to(&node, None, *v, false);
(k.clone(), node.get_id())
})
.collect(),
}
}
pub fn launch_command(self) -> Option<Command> {
let launch_info = self.launch_info.as_ref()?;
let mut cmdline = launch_info.cmdline.iter();
let mut command = Command::new(cmdline.next()?);
command.args(cmdline);
command.current_dir(&launch_info.cwd);
command.envs(launch_info.env.iter());
command.env("STARDUST_STARTUP_TOKEN", self.token());
Some(command)
}
}
impl Default for ClientStateParsed {
fn default() -> Self {
Self {
launch_info: None,
data: Default::default(),
root: Mat4::IDENTITY,
spatial_anchors: Default::default(),
}
}
}