2020-06-17 07:49:14 +00:00
|
|
|
use lazy_static::lazy_static;
|
|
|
|
use libloading::Library;
|
2020-06-18 01:16:27 +00:00
|
|
|
use notify::{immediate_watcher, EventKind, RecursiveMode, Watcher};
|
2020-06-17 07:49:14 +00:00
|
|
|
use std::{
|
|
|
|
process::{Command, Stdio},
|
2020-06-18 06:45:49 +00:00
|
|
|
sync::{mpsc, Mutex},
|
|
|
|
thread,
|
|
|
|
time::Duration,
|
2020-06-17 07:49:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
pub static ref LIB: Mutex<Option<LoadedLib>> = Mutex::new(Some(LoadedLib::compile_load()));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct LoadedLib {
|
|
|
|
pub lib: Library,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LoadedLib {
|
|
|
|
fn compile_load() -> Self {
|
|
|
|
// Compile
|
2020-06-18 06:45:49 +00:00
|
|
|
compile();
|
|
|
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
copy();
|
2020-06-17 07:49:14 +00:00
|
|
|
|
|
|
|
Self::load()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load() -> Self {
|
|
|
|
#[cfg(target_os = "windows")]
|
2020-06-18 06:45:49 +00:00
|
|
|
let lib = Library::new("../target/debug/voxygen_anim_active.dll").unwrap();
|
2020-06-17 07:49:14 +00:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
2020-06-18 00:09:12 +00:00
|
|
|
let lib = Library::new("../target/debug/libvoxygen_anim.so").unwrap();
|
2020-06-17 07:49:14 +00:00
|
|
|
|
|
|
|
Self { lib }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 01:16:27 +00:00
|
|
|
// Starts up watcher
|
2020-06-17 07:49:14 +00:00
|
|
|
pub fn init() {
|
2020-06-18 06:45:49 +00:00
|
|
|
// Make sure first compile is done
|
|
|
|
drop(LIB.lock());
|
|
|
|
|
2020-06-18 01:16:27 +00:00
|
|
|
// TODO: use crossbeam
|
2020-06-18 06:45:49 +00:00
|
|
|
let (reload_send, reload_recv) = mpsc::channel();
|
2020-06-18 01:16:27 +00:00
|
|
|
|
2020-06-17 07:49:14 +00:00
|
|
|
// Start watcher
|
2020-06-18 01:16:27 +00:00
|
|
|
let mut watcher = immediate_watcher(move |res| event_fn(res, &reload_send)).unwrap();
|
2020-06-17 07:49:14 +00:00
|
|
|
watcher.watch("src/anim", RecursiveMode::Recursive).unwrap();
|
|
|
|
|
2020-06-18 01:16:27 +00:00
|
|
|
// Start reloader that watcher signals
|
|
|
|
// "Debounces" events since I can't find the option to do this in the latest
|
|
|
|
// `notify`
|
2020-06-18 06:45:49 +00:00
|
|
|
thread::spawn(move || {
|
|
|
|
let mut modified_paths = std::collections::HashSet::new();
|
|
|
|
|
|
|
|
while let Ok(path) = reload_recv.recv() {
|
|
|
|
modified_paths.insert(path);
|
|
|
|
// Wait for to see if there are more modify events before reloading
|
|
|
|
while let Ok(path) = reload_recv.recv_timeout(Duration::from_millis(300)) {
|
|
|
|
modified_paths.insert(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut info = "Hot reloading animations because these files were modified:".to_owned();
|
|
|
|
for path in std::mem::take(&mut modified_paths) {
|
|
|
|
info.push('\n');
|
|
|
|
info.push('\"');
|
|
|
|
info.push_str(&path);
|
|
|
|
info.push('\"');
|
|
|
|
}
|
|
|
|
log::warn!("{}", info);
|
2020-06-18 01:16:27 +00:00
|
|
|
|
|
|
|
// Reload
|
|
|
|
reload();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-06-17 07:49:14 +00:00
|
|
|
// Let the watcher live forever
|
|
|
|
std::mem::forget(watcher);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recompiles and hotreloads the lib if the source is changed
|
|
|
|
// Note: designed with voxygen dir as working dir, could be made more flexible
|
2020-06-18 06:45:49 +00:00
|
|
|
fn event_fn(res: notify::Result<notify::Event>, sender: &mpsc::Sender<String>) {
|
2020-06-17 07:49:14 +00:00
|
|
|
match res {
|
|
|
|
Ok(event) => match event.kind {
|
2020-06-18 01:16:27 +00:00
|
|
|
EventKind::Modify(_) => {
|
2020-06-18 06:45:49 +00:00
|
|
|
event
|
2020-06-17 07:49:14 +00:00
|
|
|
.paths
|
|
|
|
.iter()
|
2020-06-18 06:45:49 +00:00
|
|
|
.filter(|p| p.extension().map(|e| e == "rs").unwrap_or(false))
|
|
|
|
.map(|p| p.to_string_lossy().into_owned())
|
2020-06-18 01:16:27 +00:00
|
|
|
// Signal reloader
|
2020-06-18 06:45:49 +00:00
|
|
|
.for_each(|p| { let _ = sender.send(p); });
|
2020-06-17 07:49:14 +00:00
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
},
|
2020-06-18 06:45:49 +00:00
|
|
|
Err(e) => log::error!("Animation hotreload watch error: {:?}", e),
|
2020-06-17 07:49:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reload() {
|
|
|
|
// Stop if recompile failed
|
2020-06-18 06:45:49 +00:00
|
|
|
if !compile() {
|
2020-06-17 07:49:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut lock = LIB.lock().unwrap();
|
|
|
|
|
|
|
|
// Close lib
|
|
|
|
lock.take().unwrap().lib.close().unwrap();
|
|
|
|
|
2020-06-18 06:45:49 +00:00
|
|
|
// Rename lib file on windows
|
|
|
|
// Called after closing lib so file will be unlocked
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
copy();
|
|
|
|
|
2020-06-17 07:49:14 +00:00
|
|
|
// Open new lib
|
|
|
|
*lock = Some(LoadedLib::load());
|
|
|
|
|
2020-06-18 06:45:49 +00:00
|
|
|
log::warn!("Updated animations");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns false if compile failed
|
|
|
|
fn compile() -> bool {
|
|
|
|
let output = Command::new("cargo")
|
|
|
|
.stderr(Stdio::inherit())
|
|
|
|
.stdout(Stdio::inherit())
|
|
|
|
.arg("build")
|
|
|
|
.arg("--package")
|
|
|
|
.arg("veloren-voxygen-anim")
|
|
|
|
.output()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// If compile failed
|
|
|
|
if !output.status.success() {
|
|
|
|
log::error!("Failed to compile anim crate");
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
log::warn!("Animation recompile success!!");
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy lib file if on windows since loading the lib locks the file blocking
|
|
|
|
// future compilation
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
fn copy() {
|
|
|
|
std::fs::copy(
|
|
|
|
"../target/debug/voxygen_anim.dll",
|
|
|
|
"../target/debug/voxygen_anim_active.dll",
|
|
|
|
)
|
|
|
|
.expect("Failed to rename animations dll");
|
2020-06-17 07:49:14 +00:00
|
|
|
}
|