mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-24 15:01:38 +00:00
Fiddle with reimplementing vfs to be in terms of paths; much simpler that before
This commit is contained in:
parent
a57a8e5951
commit
1896e97451
6 changed files with 255 additions and 4 deletions
|
|
@ -25,8 +25,13 @@ pub fn serve(fuzzy_project_location: &Path) {
|
||||||
|
|
||||||
roblox_studio::install_bundled_plugin().unwrap();
|
roblox_studio::install_bundled_plugin().unwrap();
|
||||||
|
|
||||||
let session = Arc::new(Session::new(project));
|
let session = Arc::new({
|
||||||
let server = Server::new(Arc::from(session));
|
let mut session = Session::new(project);
|
||||||
|
session.start().unwrap();
|
||||||
|
session
|
||||||
|
});
|
||||||
|
|
||||||
|
let server = Server::new(Arc::clone(&session));
|
||||||
|
|
||||||
println!("Server listening on port 8000.");
|
println!("Server listening on port 8000.");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,6 @@ pub mod project;
|
||||||
pub mod rbx;
|
pub mod rbx;
|
||||||
pub mod roblox_studio;
|
pub mod roblox_studio;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
pub mod vfs;
|
||||||
pub mod web;
|
pub mod web;
|
||||||
pub mod web_util;
|
pub mod web_util;
|
||||||
|
|
@ -1,20 +1,36 @@
|
||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock, Mutex, mpsc},
|
||||||
|
thread,
|
||||||
|
io,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand;
|
use rand;
|
||||||
|
|
||||||
|
use notify::{
|
||||||
|
self,
|
||||||
|
DebouncedEvent,
|
||||||
|
RecommendedWatcher,
|
||||||
|
RecursiveMode,
|
||||||
|
Watcher,
|
||||||
|
};
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
message_queue::MessageQueue,
|
message_queue::MessageQueue,
|
||||||
rbx::RbxTree,
|
rbx::RbxTree,
|
||||||
project::Project,
|
project::{Project, ProjectNode},
|
||||||
|
vfs::Vfs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const WATCH_TIMEOUT_MS: u64 = 100;
|
||||||
|
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
project: Project,
|
project: Project,
|
||||||
pub session_id: String,
|
pub session_id: String,
|
||||||
pub message_queue: Arc<MessageQueue>,
|
pub message_queue: Arc<MessageQueue>,
|
||||||
pub tree: Arc<RwLock<RbxTree>>,
|
pub tree: Arc<RwLock<RbxTree>>,
|
||||||
|
vfs: Arc<Mutex<Vfs>>,
|
||||||
|
watchers: Vec<RecommendedWatcher>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
|
|
@ -26,9 +42,81 @@ impl Session {
|
||||||
project,
|
project,
|
||||||
message_queue: Arc::new(MessageQueue::new()),
|
message_queue: Arc::new(MessageQueue::new()),
|
||||||
tree: Arc::new(RwLock::new(RbxTree::new())),
|
tree: Arc::new(RwLock::new(RbxTree::new())),
|
||||||
|
vfs: Arc::new(Mutex::new(Vfs::new())),
|
||||||
|
watchers: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) -> io::Result<()> {
|
||||||
|
fn add_sync_points(vfs: &mut Vfs, project_node: &ProjectNode) -> io::Result<()> {
|
||||||
|
match project_node {
|
||||||
|
ProjectNode::Regular { children, .. } => {
|
||||||
|
for child in children.values() {
|
||||||
|
add_sync_points(vfs, child)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ProjectNode::SyncPoint { path } => {
|
||||||
|
vfs.add_root(path)?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut vfs = self.vfs.lock().unwrap();
|
||||||
|
|
||||||
|
for child in self.project.tree.values() {
|
||||||
|
add_sync_points(&mut vfs, child)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for root in vfs.get_roots() {
|
||||||
|
println!("Watching {}", root.display());
|
||||||
|
|
||||||
|
let (watch_tx, watch_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let mut watcher = notify::watcher(watch_tx, Duration::from_millis(WATCH_TIMEOUT_MS)).unwrap();
|
||||||
|
|
||||||
|
watcher.watch(root, RecursiveMode::Recursive).unwrap();
|
||||||
|
self.watchers.push(watcher);
|
||||||
|
|
||||||
|
let vfs = Arc::clone(&self.vfs);
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
println!("Thread started");
|
||||||
|
loop {
|
||||||
|
match watch_rx.recv() {
|
||||||
|
Ok(event) => {
|
||||||
|
println!("Event: {:?}", event);
|
||||||
|
|
||||||
|
match event {
|
||||||
|
DebouncedEvent::Create(path) | DebouncedEvent::Write(path) => {
|
||||||
|
let mut vfs = vfs.lock().unwrap();
|
||||||
|
vfs.add_or_update(&path).unwrap();
|
||||||
|
},
|
||||||
|
DebouncedEvent::Remove(path) => {
|
||||||
|
let mut vfs = vfs.lock().unwrap();
|
||||||
|
vfs.remove(&path);
|
||||||
|
},
|
||||||
|
DebouncedEvent::Rename(from_path, to_path) => {
|
||||||
|
let mut vfs = vfs.lock().unwrap();
|
||||||
|
vfs.remove(&from_path);
|
||||||
|
vfs.add_or_update(&to_path).unwrap();
|
||||||
|
},
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
println!("Thread stopped");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_project(&self) -> &Project {
|
pub fn get_project(&self) -> &Project {
|
||||||
&self.project
|
&self.project
|
||||||
}
|
}
|
||||||
|
|
|
||||||
146
server/src/vfs.rs
Normal file
146
server/src/vfs.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
fs,
|
||||||
|
io,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Vfs {
|
||||||
|
contents: HashMap<PathBuf, Vec<u8>>,
|
||||||
|
items: HashMap<PathBuf, VfsItem>,
|
||||||
|
roots: HashSet<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vfs {
|
||||||
|
pub fn new() -> Vfs {
|
||||||
|
Vfs {
|
||||||
|
contents: HashMap::new(),
|
||||||
|
items: HashMap::new(),
|
||||||
|
roots: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_root<'a, 'b>(&'a mut self, root_path: &'b Path) -> io::Result<&'a VfsItem> {
|
||||||
|
debug_assert!(root_path.is_absolute());
|
||||||
|
|
||||||
|
self.roots.insert(root_path.to_path_buf());
|
||||||
|
|
||||||
|
VfsItem::get(self, root_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_roots(&self) -> &HashSet<PathBuf> {
|
||||||
|
&self.roots
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, path: &Path) -> Option<&VfsItem> {
|
||||||
|
debug_assert!(path.is_absolute());
|
||||||
|
debug_assert!(self.is_valid_path(path));
|
||||||
|
|
||||||
|
self.items.get(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, path: &Path) {
|
||||||
|
debug_assert!(path.is_absolute());
|
||||||
|
debug_assert!(self.is_valid_path(path));
|
||||||
|
|
||||||
|
match self.items.remove(path) {
|
||||||
|
Some(item) => match item {
|
||||||
|
VfsItem::File(_) => {
|
||||||
|
self.contents.remove(path);
|
||||||
|
},
|
||||||
|
VfsItem::Directory(VfsDirectory { children, .. }) => {
|
||||||
|
for child_path in &children {
|
||||||
|
self.remove(child_path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_or_update<'a, 'b>(&'a mut self, path: &'b Path) -> io::Result<&'a VfsItem> {
|
||||||
|
debug_assert!(path.is_absolute());
|
||||||
|
debug_assert!(self.is_valid_path(path));
|
||||||
|
|
||||||
|
VfsItem::get(self, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_path(&self, path: &Path) -> bool {
|
||||||
|
let mut is_valid_path = false;
|
||||||
|
|
||||||
|
for root_path in &self.roots {
|
||||||
|
if path.starts_with(root_path) {
|
||||||
|
is_valid_path = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VfsFile {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsFile {
|
||||||
|
pub fn read_contents<'file, 'vfs>(&'file self, vfs: &'vfs mut Vfs) -> io::Result<&'vfs [u8]> {
|
||||||
|
if !vfs.contents.contains_key(&self.path) {
|
||||||
|
let contents = fs::read(&self.path)?;
|
||||||
|
vfs.contents.insert(self.path.clone(), contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vfs.contents.get(&self.path).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VfsDirectory {
|
||||||
|
path: PathBuf,
|
||||||
|
children: HashSet<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum VfsItem {
|
||||||
|
File(VfsFile),
|
||||||
|
Directory(VfsDirectory),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsItem {
|
||||||
|
fn get<'a, 'b>(vfs: &'a mut Vfs, root_path: &'b Path) -> io::Result<&'a VfsItem> {
|
||||||
|
let metadata = fs::metadata(root_path)?;
|
||||||
|
|
||||||
|
if metadata.is_file() {
|
||||||
|
let item = VfsItem::File(VfsFile {
|
||||||
|
path: root_path.to_path_buf(),
|
||||||
|
});
|
||||||
|
|
||||||
|
vfs.items.insert(root_path.to_path_buf(), item);
|
||||||
|
|
||||||
|
let contents = fs::read(root_path)?;
|
||||||
|
vfs.contents.insert(root_path.to_path_buf(), contents);
|
||||||
|
|
||||||
|
Ok(vfs.items.get(root_path).unwrap())
|
||||||
|
} else if metadata.is_dir() {
|
||||||
|
let mut children = HashSet::new();
|
||||||
|
|
||||||
|
for entry in fs::read_dir(root_path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
VfsItem::get(vfs, &path)?;
|
||||||
|
|
||||||
|
children.insert(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = VfsItem::Directory(VfsDirectory {
|
||||||
|
path: root_path.to_path_buf(),
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
|
||||||
|
vfs.items.insert(root_path.to_path_buf(), item);
|
||||||
|
|
||||||
|
Ok(vfs.items.get(root_path).unwrap())
|
||||||
|
} else {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
0
test-projects/single-sync-point/lib/main.lua
Normal file
0
test-projects/single-sync-point/lib/main.lua
Normal file
11
test-projects/single-sync-point/roblox-project.json
Normal file
11
test-projects/single-sync-point/roblox-project.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "empty",
|
||||||
|
"tree": {
|
||||||
|
"ReplicatedStorage": {
|
||||||
|
"$className": "ReplicatedStorage",
|
||||||
|
"Foo": {
|
||||||
|
"$path": "lib"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue