veloren_common_assets/
fs.rs1use std::{fs, io};
2
3use assets_manager::{
4 BoxedError,
5 hot_reloading::{EventSender, FsWatcherBuilder},
6 source::{DirEntry, FileContent, FileSystem as RawFs, Source},
7};
8
9#[derive(Debug, Clone)]
12pub struct FileSystem {
13 default: RawFs,
14 override_dir: Option<RawFs>,
15}
16
17impl FileSystem {
18 pub fn new() -> io::Result<Self> {
19 let default = RawFs::new(&*super::ASSETS_PATH)?;
20 let override_dir = std::env::var_os("VELOREN_ASSETS_OVERRIDE").and_then(|path| {
21 RawFs::new(path)
22 .map_err(|err| tracing::error!("Error setting override assets directory: {}", err))
23 .ok()
24 });
25
26 let canary = fs::read_to_string(super::ASSETS_PATH.join("common").join("canary.canary"))
27 .map_err(|e| io::Error::other(format!("failed to load canary asset: {}", e)))?;
28
29 if !canary.starts_with("VELOREN_CANARY_MAGIC") {
30 panic!("Canary asset `canary.canary` was present but did not contain the expected data. This *heavily* implies that you've not correctly set up Git LFS (Large File Storage). Visit `https://book.veloren.net/contributors/development-tools.html#git-lfs` for more information about setting up Git LFS.");
31 }
32
33 Ok(Self {
34 default,
35 override_dir,
36 })
37 }
38}
39
40impl Source for FileSystem {
41 fn read(&self, id: &str, ext: &str) -> io::Result<FileContent<'_>> {
42 if let Some(dir) = &self.override_dir {
43 match dir.read(id, ext) {
44 Ok(content) => return Ok(content),
45 Err(err) => {
46 if err.kind() != io::ErrorKind::NotFound {
47 let path = dir.path_of(DirEntry::File(id, ext));
48 tracing::warn!(
49 "Error reading \"{}\": {}. Falling back to default",
50 path.display(),
51 err
52 );
53 }
54 },
55 }
56 }
57
58 self.default.read(id, ext)
60 }
61
62 fn read_dir(&self, id: &str, f: &mut dyn FnMut(DirEntry)) -> io::Result<()> {
63 if let Some(dir) = &self.override_dir {
64 match dir.read_dir(id, f) {
65 Ok(()) => return Ok(()),
66 Err(err) => {
67 if err.kind() != io::ErrorKind::NotFound {
68 let path = dir.path_of(DirEntry::Directory(id));
69 tracing::warn!(
70 "Error reading \"{}\": {}. Falling back to default",
71 path.display(),
72 err
73 );
74 }
75 },
76 }
77 }
78
79 self.default.read_dir(id, f)
81 }
82
83 fn exists(&self, entry: DirEntry) -> bool {
84 self.override_dir
85 .as_ref()
86 .is_some_and(|dir| dir.exists(entry))
87 || self.default.exists(entry)
88 }
89
90 fn configure_hot_reloading(&self, events: EventSender) -> Result<(), BoxedError> {
91 let mut builder = FsWatcherBuilder::new()?;
92
93 if let Some(dir) = &self.override_dir {
94 builder.watch(dir.root().to_owned())?;
95 }
96 builder.watch(self.default.root().to_owned())?;
97
98 builder.build(events);
99 Ok(())
100 }
101}