use super::spatial::Spatial; use super::{Aspect, AspectIdentifier, Node}; use crate::core::client::Client; use crate::core::client_state::ClientStateParsed; use crate::core::registry::Registry; use crate::nodes::spatial::SPATIAL_REF_ASPECT_ALIAS_INFO; use crate::session::connection_env; use color_eyre::eyre::{bail, Result}; use glam::Mat4; use std::path::PathBuf; use std::sync::Arc; use std::time::Instant; use tracing::info; static ROOT_REGISTRY: Registry = Registry::new(); stardust_xr_server_codegen::codegen_root_protocol!(); pub struct Root { node: Arc, connect_instant: Instant, } impl Root { pub fn create(client: &Arc, transform: Mat4) -> Result> { let node = Node::from_id(client, 0, false); let node = node.add_to_scenegraph()?; let _ = Spatial::add_to(&node, None, transform, false); let root_aspect = node.add_aspect(Root { node: node.clone(), connect_instant: Instant::now(), }); ROOT_REGISTRY.add_raw(&root_aspect); Ok(root_aspect) } pub fn send_frame_events(delta: f64) { for root in ROOT_REGISTRY.get_valid_contents() { let _ = root_client::frame( &root.node, &FrameInfo { delta: delta as f32, elapsed: root.connect_instant.elapsed().as_secs_f32(), }, ); } } pub fn set_transform(&self, transform: Mat4) { let spatial = self.node.get_aspect::().unwrap(); spatial.set_spatial_parent(None).unwrap(); spatial.set_local_transform(transform); } pub async fn save_state(&self) -> Result { Ok(root_client::save_state(&self.node).await?.0) } } impl AspectIdentifier for Root { impl_aspect_for_root_aspect_id! {} } impl Aspect for Root { impl_aspect_for_root_aspect! {} } impl RootAspect for Root { async fn get_state(_node: Arc, calling_client: Arc) -> Result { let Some(state) = calling_client.state.get() else { bail!("Couldn't get state"); }; Ok(state.clone()) } #[doc = "Get a hashmap of all the environment variables to connect a given app to the stardust server"] async fn get_connection_environment( _node: Arc, _calling_client: Arc, ) -> Result> { Ok(connection_env()) } #[doc = "Generate a client state token and return it back.\n\n When launching a new client, set the environment variable `STARDUST_STARTUP_TOKEN` to the returned string.\n Make sure the environment variable shows in `/proc/{pid}/environ` as that's the only reliable way to pass the value to the server (suggestions welcome).\n"] async fn generate_state_token( _node: Arc, calling_client: Arc, state: ClientState, ) -> Result { Ok(ClientStateParsed::from_deserialized(&calling_client, state).token()) } #[doc = "Set initial list of folders to look for namespaced resources in"] fn set_base_prefixes( _node: Arc, calling_client: Arc, prefixes: Vec, ) -> Result<()> { info!(?calling_client, ?prefixes, "Set base prefixes"); *calling_client.base_resource_prefixes.lock() = prefixes.into_iter().map(PathBuf::from).collect(); Ok(()) } #[doc = "Cleanly disconnect from the server"] fn disconnect(_node: Arc, calling_client: Arc) -> color_eyre::eyre::Result<()> { calling_client.disconnect(Ok(())); Ok(()) } } impl Drop for Root { fn drop(&mut self) { ROOT_REGISTRY.remove(self); } }