feat: client state (save/restore)

This commit is contained in:
Nova
2023-09-28 09:55:45 -04:00
parent af75d2a451
commit fc45b4e400
10 changed files with 266 additions and 235 deletions

View File

@@ -1,20 +1,20 @@
use super::scenegraph::Scenegraph;
use super::{
client_state::{ClientState, CLIENT_STATES},
scenegraph::Scenegraph,
};
use crate::{
core::{registry::OwnedRegistry, task},
nodes::{
audio, data, drawable, fields, hmd, input, items,
root::Root,
spatial,
startup::{self, StartupSettings, STARTUP_SETTINGS},
Node,
},
nodes::{audio, data, drawable, fields, hmd, input, items, root::Root, spatial, Node},
};
use color_eyre::eyre::{eyre, Result};
use lazy_static::lazy_static;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use stardust_xr::messenger::{self, MessageSenderHandle};
use stardust_xr::{
messenger::{self, MessageSenderHandle},
schemas::flex::serialize,
};
use std::{fs, iter::FromIterator, path::PathBuf, sync::Arc};
use tokio::{net::UnixStream, task::JoinHandle};
use tracing::info;
@@ -34,7 +34,7 @@ lazy_static! {
scenegraph: Default::default(),
root: OnceCell::new(),
base_resource_prefixes: Default::default(),
startup_settings: None,
state: Arc::new(ClientState::default()),
});
}
@@ -46,9 +46,9 @@ pub fn get_env(pid: i32) -> Result<FxHashMap<String, String>, std::io::Error> {
.map(|(k, v)| (k.to_string(), v.to_string())),
))
}
pub fn startup_settings(env: &FxHashMap<String, String>) -> Option<StartupSettings> {
pub fn state(env: &FxHashMap<String, String>) -> Option<Arc<ClientState>> {
let token = env.get("STARDUST_STARTUP_TOKEN")?;
STARTUP_SETTINGS.lock().get(token).cloned()
CLIENT_STATES.lock().get(token).cloned()
}
pub struct Client {
@@ -63,7 +63,7 @@ pub struct Client {
pub scenegraph: Arc<Scenegraph>,
pub root: OnceCell<Arc<Root>>,
pub base_resource_prefixes: Mutex<Vec<PathBuf>>,
pub startup_settings: Option<StartupSettings>,
pub state: Arc<ClientState>,
}
impl Client {
pub fn from_connection(connection: UnixStream) -> Result<Arc<Self>> {
@@ -80,7 +80,10 @@ impl Client {
let (mut messenger_tx, mut messenger_rx) = messenger::create(connection);
let scenegraph = Arc::new(Scenegraph::default());
let startup_settings = env.as_ref().and_then(startup_settings);
let state = env
.as_ref()
.and_then(state)
.unwrap_or_else(|| Arc::new(ClientState::default()));
let client = CLIENTS.add(Client {
pid,
@@ -95,7 +98,7 @@ impl Client {
scenegraph: scenegraph.clone(),
root: OnceCell::new(),
base_resource_prefixes: Default::default(),
startup_settings,
state,
});
let _ = client.scenegraph.client.set(Arc::downgrade(&client));
let _ = client.root.set(Root::create(&client)?);
@@ -107,7 +110,13 @@ impl Client {
data::create_interface(&client)?;
items::create_interface(&client)?;
input::create_interface(&client)?;
startup::create_interface(&client)?;
client
.root
.get()
.unwrap()
.node
.send_remote_signal("restore_state", serialize(client.state.apply_to(&client))?)?;
let pid_printable = pid
.map(|pid| pid.to_string())
@@ -164,6 +173,27 @@ impl Client {
Ok(client)
}
pub fn get_cmdline(&self) -> Option<Vec<String>> {
let pid = self.pid?;
let exe_proc_path = format!("/proc/{pid}/exe");
let cmdline_proc_path = format!("/proc/{pid}/cmdline");
let exe = std::fs::read_link(exe_proc_path).ok()?;
let cmdline = std::fs::read_to_string(cmdline_proc_path).ok()?;
let mut cmdline_split: Vec<_> = cmdline.split('\0').map(ToString::to_string).collect();
cmdline_split.pop();
*cmdline_split.get_mut(0).unwrap() = exe.to_str()?.to_string();
Some(cmdline_split)
}
pub fn get_cwd(&self) -> Option<PathBuf> {
let pid = self.pid?;
let cwd_proc_path = format!("/proc/{pid}/cwd");
std::fs::read_link(cwd_proc_path).ok()
}
pub async fn save_state(&self) -> Option<ClientState> {
let internal = self.root.get()?.save_state().await.ok()?;
Some(dbg!(ClientState::from_deserialized(self, internal)))
}
#[inline]
pub fn get_node(&self, name: &'static str, path: &str) -> Result<Arc<Node>> {
self.scenegraph

94
src/core/client_state.rs Normal file
View File

@@ -0,0 +1,94 @@
use super::client::Client;
use crate::nodes::{spatial::Spatial, Node};
use glam::Mat4;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::{path::PathBuf, sync::Arc};
lazy_static::lazy_static! {
pub static ref CLIENT_STATES: Mutex<FxHashMap<String, Arc<ClientState>>> = Default::default();
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ClientState {
pub cmdline: Option<Vec<String>>,
pub cwd: Option<PathBuf>,
pub data: Option<Vec<u8>>,
pub root: Mat4,
pub spatial_anchors: FxHashMap<String, Mat4>,
}
impl ClientState {
pub fn from_deserialized(client: &Client, state: ClientStateInternal) -> Self {
ClientState {
cmdline: client.get_cmdline(),
cwd: client.get_cwd(),
data: state.data,
root: Self::spatial_transform(client, &state.root.unwrap_or_default())
.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, path: &str) -> Option<Mat4> {
let node = client.scenegraph.get_node(path)?;
let spatial = node.spatial.get()?;
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 to_file(self) {
let state_dir = directories::ProjectDirs::from("", "", "stardust")
.unwrap()
.state_dir()
.unwrap();
// std::fs::File::create(state_dir.join(""))
}
pub fn apply_to(&self, client: &Arc<Client>) -> ClientStateInternal {
if let Some(root) = client.root.get() {
root.set_transform(self.root)
}
ClientStateInternal {
data: self.data.clone(),
root: Some("/".to_string()),
spatial_anchors: self
.spatial_anchors
.iter()
.map(|(k, v)| {
(k.clone(), {
let node = Node::create(client, "/spatial/anchor", k, true)
.add_to_scenegraph()
.unwrap();
Spatial::add_to(&node, None, *v, false).unwrap();
k.clone()
})
})
.collect(),
}
}
}
impl Default for ClientState {
fn default() -> Self {
Self {
cmdline: None,
cwd: None,
data: None,
root: Mat4::IDENTITY,
spatial_anchors: Default::default(),
}
}
}
#[derive(Default, Serialize, Deserialize)]
pub struct ClientStateInternal {
data: Option<Vec<u8>>,
root: Option<String>,
spatial_anchors: FxHashMap<String, String>,
}

View File

@@ -1,4 +1,5 @@
pub mod client;
pub mod client_state;
pub mod delta;
pub mod destroy_queue;
pub mod eventloop;