mirror of
https://gitlab.com/veloren/veloren
synced 2026-08-16 16:26:07 -04:00
Remove asset_tweak
This commit is contained in:
parent
9842265721
commit
9263b50b0f
5 changed files with 10 additions and 485 deletions
|
|
@ -8,7 +8,8 @@ rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
|||
[target.x86_64-pc-windows-gnu]
|
||||
rustflags = [
|
||||
# Required for mimalloc
|
||||
"-C", "link-arg=-lpsapi",
|
||||
"-C",
|
||||
"link-arg=-lpsapi",
|
||||
]
|
||||
|
||||
# Clever way to get workspace dir, from https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993
|
||||
|
|
@ -37,5 +38,5 @@ tracy-voxygen = "run --bin veloren-voxygen --no-default-features --features trac
|
|||
dbg-voxygen = "run --bin veloren-voxygen --profile debuginfo"
|
||||
# misc
|
||||
swarm = "run --bin swarm --features client/bin_bot,client/tick_network --"
|
||||
ci-clippy = "clippy --all-targets --locked --features=bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,asset_tweak,bin,stat"
|
||||
ci-clippy = "clippy --all-targets --locked --features=bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,bin,stat"
|
||||
ci-clippy2 = "clippy -p veloren-voxygen --locked --no-default-features --features=default-publish"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
time cargo clippy \
|
||||
--all-targets \
|
||||
--locked \
|
||||
--features="bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,asset_tweak,bin,stat,cli" \
|
||||
--features="bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,bin,stat,cli" \
|
||||
-- -D warnings &&
|
||||
|
||||
# Ensure that the veloren-voxygen default-publish feature builds as it excludes some default features.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,4 @@
|
|||
VELOREN_ASSETS="$(pwd)/assets";
|
||||
export VELOREN_ASSETS;
|
||||
|
||||
time cargo test \
|
||||
--package veloren-common-assets asset_tweak::tests \
|
||||
--features asset_tweak --lib &&
|
||||
time cargo test;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ workspace = true
|
|||
|
||||
[dependencies]
|
||||
lazy_static = { workspace = true }
|
||||
assets_manager = { version = "0.13", features = ["ab_glyph", "bincode", "ron", "json"] }
|
||||
assets_manager = { version = "0.13", features = [
|
||||
"ab_glyph",
|
||||
"bincode",
|
||||
"ron",
|
||||
"json",
|
||||
] }
|
||||
ron = { workspace = true }
|
||||
dot_vox = "5.1"
|
||||
wavefront = "0.2" # TODO: Use vertex-colors branch when we have models that have them
|
||||
|
|
@ -26,5 +31,4 @@ walkdir = "2.3.2"
|
|||
|
||||
[features]
|
||||
hot-reloading = ["assets_manager/hot-reloading"]
|
||||
asset_tweak = ["dep:serde", "hot-reloading"]
|
||||
plugins = ["dep:serde", "assets_manager/tar"]
|
||||
|
|
|
|||
|
|
@ -438,480 +438,3 @@ mod tests {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "asset_tweak")]
|
||||
pub mod asset_tweak {
|
||||
//! Set of functions and macros for easy tweaking values
|
||||
//! using our asset cache machinery.
|
||||
//!
|
||||
//! Because of how macros works, you will not find
|
||||
//! [tweak] and [tweak_from] macros in this module,
|
||||
//! import it from [assets](super) crate directly.
|
||||
//!
|
||||
//! Will hot-reload (if corresponded feature is enabled).
|
||||
// TODO: don't use the same ASSETS_PATH as game uses?
|
||||
use super::{ASSETS_PATH, AssetExt, Ron};
|
||||
use ron::{options::Options, ser::PrettyConfig};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::{fs, path::Path};
|
||||
|
||||
/// Specifier to use with tweak functions in this module
|
||||
///
|
||||
/// `Tweak("test")` will be interpreted as `<assets_dir>/tweak/test.ron`.
|
||||
///
|
||||
/// `Asset(&["path", "to", "file"])` will be interpreted as
|
||||
/// `<assets_dir>/path/to/file.ron`
|
||||
pub enum Specifier<'a> {
|
||||
Tweak(&'a str),
|
||||
Asset(&'a [&'a str]),
|
||||
}
|
||||
|
||||
/// Read value from file, will panic if file doesn't exist.
|
||||
///
|
||||
/// If you don't have a file or its content is invalid,
|
||||
/// this function will panic.
|
||||
/// If you want to have some default content,
|
||||
/// read documentation for [tweak_expect_or_create] for more.
|
||||
///
|
||||
/// # Examples:
|
||||
/// How not to use.
|
||||
/// ```should_panic
|
||||
/// use veloren_common_assets::asset_tweak::{Specifier, tweak_expect};
|
||||
///
|
||||
/// // will panic if you don't have a file
|
||||
/// let specifier = Specifier::Asset(&["no_way_we_have_this_directory", "x"]);
|
||||
/// let x: i32 = tweak_expect(specifier);
|
||||
/// ```
|
||||
///
|
||||
/// How to use.
|
||||
/// ```
|
||||
/// use std::fs;
|
||||
/// use veloren_common_assets::{
|
||||
/// ASSETS_PATH,
|
||||
/// asset_tweak::{Specifier, tweak_expect},
|
||||
/// };
|
||||
///
|
||||
/// // you need to create file first
|
||||
/// let tweak_path = ASSETS_PATH.join("tweak/year.ron");
|
||||
/// // note lack of parentheses
|
||||
/// fs::write(&tweak_path, b"10");
|
||||
///
|
||||
/// let y: i32 = tweak_expect(Specifier::Tweak("year"));
|
||||
/// assert_eq!(y, 10);
|
||||
///
|
||||
/// // Specifier::Tweak is just a shorthand
|
||||
/// // for Specifier::Asset(&["tweak", ..])
|
||||
/// let y1: i32 = tweak_expect(Specifier::Asset(&["tweak", "year"]));
|
||||
/// assert_eq!(y1, 10);
|
||||
///
|
||||
/// // you may want to remove this file later
|
||||
/// fs::remove_file(tweak_path);
|
||||
/// ```
|
||||
pub fn tweak_expect<T>(specifier: Specifier) -> T
|
||||
where
|
||||
T: Clone + Sized + Send + Sync + 'static + DeserializeOwned,
|
||||
{
|
||||
let asset_specifier = match specifier {
|
||||
Specifier::Tweak(specifier) => format!("tweak.{}", specifier),
|
||||
Specifier::Asset(path) => path.join("."),
|
||||
};
|
||||
let handle = <Ron<T> as AssetExt>::load_expect(&asset_specifier);
|
||||
let Ron(value) = handle.cloned();
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
// Helper function to create new file to tweak.
|
||||
//
|
||||
// The file will be filled with passed value
|
||||
// returns passed value.
|
||||
fn create_new<T>(tweak_dir: &Path, filename: &str, value: T) -> T
|
||||
where
|
||||
T: Sized + Send + Sync + 'static + DeserializeOwned + Serialize,
|
||||
{
|
||||
fs::create_dir_all(tweak_dir).expect("failed to create directory for tweak files");
|
||||
let f = fs::File::create(tweak_dir.join(filename)).unwrap_or_else(|error| {
|
||||
panic!("failed to create file {:?}. Error: {:?}", filename, error)
|
||||
});
|
||||
let tweaker = Ron(&value);
|
||||
if let Err(e) = Options::default().to_io_writer_pretty(f, &tweaker, PrettyConfig::new()) {
|
||||
panic!("failed to write to file {:?}. Error: {:?}", filename, e);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
// Helper function to get directory and file from asset list.
|
||||
//
|
||||
// Converts ["path", "to", "file"] to (String("path/to"), "file")
|
||||
fn directory_and_name<'a>(path: &'a [&'a str]) -> (String, &'a str) {
|
||||
let (file, path) = path.split_last().expect("empty asset list");
|
||||
let directory = path.join("/");
|
||||
|
||||
(directory, file)
|
||||
}
|
||||
|
||||
/// Read a value from asset, creating file if not exists.
|
||||
///
|
||||
/// If file exists will read a value from such file
|
||||
/// using [tweak_expect].
|
||||
///
|
||||
/// File should look like that (note the lack of parentheses).
|
||||
/// ```text
|
||||
/// assets/tweak/x.ron
|
||||
/// 5
|
||||
/// ```
|
||||
///
|
||||
/// # Example:
|
||||
/// Tweaking integer value
|
||||
/// ```
|
||||
/// use veloren_common_assets::{
|
||||
/// ASSETS_PATH,
|
||||
/// asset_tweak::{Specifier, tweak_expect_or_create},
|
||||
/// };
|
||||
///
|
||||
/// // first time it will create the file
|
||||
/// let x: i32 = tweak_expect_or_create(Specifier::Tweak("stars"), 5);
|
||||
/// let file_path = ASSETS_PATH.join("tweak/stars.ron");
|
||||
/// assert!(file_path.is_file());
|
||||
/// assert_eq!(x, 5);
|
||||
///
|
||||
/// // next time it will read value from file
|
||||
/// // whatever you will pass as default
|
||||
/// let x1: i32 = tweak_expect_or_create(Specifier::Tweak("stars"), 42);
|
||||
/// assert_eq!(x1, 5);
|
||||
///
|
||||
/// // you may want to remove this file later
|
||||
/// std::fs::remove_file(file_path);
|
||||
/// ```
|
||||
pub fn tweak_expect_or_create<T>(specifier: Specifier, value: T) -> T
|
||||
where
|
||||
T: Clone + Sized + Send + Sync + 'static + DeserializeOwned + Serialize,
|
||||
{
|
||||
let (dir, filename) = match specifier {
|
||||
Specifier::Tweak(name) => (ASSETS_PATH.join("tweak"), format!("{}.ron", name)),
|
||||
Specifier::Asset(list) => {
|
||||
let (directory, name) = directory_and_name(list);
|
||||
(ASSETS_PATH.join(directory), format!("{}.ron", name))
|
||||
},
|
||||
};
|
||||
|
||||
if Path::new(&dir.join(&filename)).is_file() {
|
||||
tweak_expect(specifier)
|
||||
} else {
|
||||
create_new(&dir, &filename, value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenient macro to quickly tweak value.
|
||||
///
|
||||
/// Will use [Specifier]`::Tweak` specifier and call
|
||||
/// [tweak_expect] if passed only name
|
||||
/// or [tweak_expect_or_create] if default is passed.
|
||||
///
|
||||
/// # Examples:
|
||||
/// ```
|
||||
/// // note that you need to export it from `assets` crate,
|
||||
/// // not from `assets::asset_tweak`
|
||||
/// use veloren_common_assets::{ASSETS_PATH, tweak};
|
||||
///
|
||||
/// // you need to create file first
|
||||
/// let own_path = ASSETS_PATH.join("tweak/grizelda.ron");
|
||||
/// // note lack of parentheses
|
||||
/// std::fs::write(&own_path, b"10");
|
||||
///
|
||||
/// let z: i32 = tweak!("grizelda");
|
||||
/// assert_eq!(z, 10);
|
||||
///
|
||||
/// // voila, you don't need to care about creating file first
|
||||
/// let p: i32 = tweak!("peter", 8);
|
||||
///
|
||||
/// let created_path = ASSETS_PATH.join("tweak/peter.ron");
|
||||
/// assert!(created_path.is_file());
|
||||
/// assert_eq!(p, 8);
|
||||
///
|
||||
/// // will use default value only first time
|
||||
/// // if file exists, will load from this file
|
||||
/// let p: i32 = tweak!("peter", 50);
|
||||
/// assert_eq!(p, 8);
|
||||
///
|
||||
/// // you may want to remove this file later
|
||||
/// std::fs::remove_file(own_path);
|
||||
/// std::fs::remove_file(created_path);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tweak {
|
||||
($name:literal) => {{
|
||||
use $crate::asset_tweak::{Specifier::Tweak, tweak_expect};
|
||||
|
||||
tweak_expect(Tweak($name))
|
||||
}};
|
||||
|
||||
($name:literal, $default:expr) => {{
|
||||
use $crate::asset_tweak::{Specifier::Tweak, tweak_expect_or_create};
|
||||
|
||||
tweak_expect_or_create(Tweak($name), $default)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Convenient macro to quickly tweak value from some existing path.
|
||||
///
|
||||
/// Will use [Specifier]`::Asset` specifier and call
|
||||
/// [tweak_expect] if passed only name
|
||||
/// or [tweak_expect_or_create] if default is passed.
|
||||
///
|
||||
/// The main use case is when you have some object
|
||||
/// which needs constant tuning of values, but you can't afford
|
||||
/// loading a file.
|
||||
/// So you can use tweak_from! and then just copy values from asset
|
||||
/// to your object.
|
||||
///
|
||||
/// # Examples:
|
||||
/// ```no_run
|
||||
/// // note that you need to export it from `assets` crate,
|
||||
/// // not from `assets::asset_tweak`
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use veloren_common_assets::{ASSETS_PATH, tweak_from};
|
||||
///
|
||||
/// #[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||
/// struct Data {
|
||||
/// x: i32,
|
||||
/// y: i32,
|
||||
/// }
|
||||
///
|
||||
/// let default = Data { x: 5, y: 7 };
|
||||
/// let data: Data = tweak_from!(&["common", "body", "dimensions"], default);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tweak_from {
|
||||
($path:expr) => {{
|
||||
use $crate::asset_tweak::{Specifier::Asset, tweak_expect};
|
||||
|
||||
tweak_expect(Asset($path))
|
||||
}};
|
||||
|
||||
($path:expr, $default:expr) => {{
|
||||
use $crate::asset_tweak::{Specifier::Asset, tweak_expect_or_create};
|
||||
|
||||
tweak_expect_or_create(Asset($path), $default)
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
convert::AsRef,
|
||||
fmt::Debug,
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
struct DirectoryGuard<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
dir: P,
|
||||
}
|
||||
|
||||
impl<P> DirectoryGuard<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn create(dir: P) -> Self {
|
||||
fs::create_dir_all(&dir).expect("failed to create directory");
|
||||
Self { dir }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Drop for DirectoryGuard<P>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn drop(&mut self) { fs::remove_dir(&self.dir).expect("failed to remove directory"); }
|
||||
}
|
||||
|
||||
struct FileGuard<P>
|
||||
where
|
||||
P: AsRef<Path> + Debug,
|
||||
{
|
||||
file: P,
|
||||
}
|
||||
|
||||
impl<P> FileGuard<P>
|
||||
where
|
||||
P: AsRef<Path> + Debug,
|
||||
{
|
||||
fn create(file: P) -> (Self, File) {
|
||||
let f = File::create(&file)
|
||||
.unwrap_or_else(|_| panic!("failed to create file {:?}", file));
|
||||
(Self { file }, f)
|
||||
}
|
||||
|
||||
fn hold(file: P) -> Self { Self { file } }
|
||||
}
|
||||
|
||||
impl<P> Drop for FileGuard<P>
|
||||
where
|
||||
P: AsRef<Path> + Debug,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
fs::remove_file(&self.file).unwrap_or_else(|e| {
|
||||
panic!("failed to remove file {:?}. Error: {:?}", self.file, e)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to create environment with needed directory and file
|
||||
// and responsible for cleaning
|
||||
fn run_with_file(tweak_path: &[&str], test: impl Fn(&mut File)) {
|
||||
let (tweak_dir, tweak_name) = directory_and_name(tweak_path);
|
||||
let tweak_folder = ASSETS_PATH.join(tweak_dir);
|
||||
let tweak_file = tweak_folder.join(format!("{}.ron", tweak_name));
|
||||
|
||||
let _dir_guard = DirectoryGuard::create(tweak_folder);
|
||||
let (_file_guard, mut file) = FileGuard::create(tweak_file);
|
||||
|
||||
test(&mut file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tweaked_int() {
|
||||
let tweak_path = &["tweak_test_int", "tweak"];
|
||||
|
||||
run_with_file(tweak_path, |file| {
|
||||
file.write_all(b"5").expect("failed to write to the file");
|
||||
let x: i32 = tweak_expect(Specifier::Asset(tweak_path));
|
||||
assert_eq!(x, 5);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tweaked_string() {
|
||||
let tweak_path = &["tweak_test_string", "tweak"];
|
||||
|
||||
run_with_file(tweak_path, |file| {
|
||||
file.write_all(br#""Hello Zest""#)
|
||||
.expect("failed to write to the file");
|
||||
|
||||
let x: String = tweak_expect(Specifier::Asset(tweak_path));
|
||||
assert_eq!(x, "Hello Zest".to_owned());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tweaked_hashmap() {
|
||||
type Map = std::collections::HashMap<String, i32>;
|
||||
|
||||
let tweak_path = &["tweak_test_map", "tweak"];
|
||||
|
||||
run_with_file(tweak_path, |file| {
|
||||
file.write_all(
|
||||
br#"
|
||||
{
|
||||
"wow": 4,
|
||||
"such": 5,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.expect("failed to write to the file");
|
||||
|
||||
let x: Map = tweak_expect(Specifier::Asset(tweak_path));
|
||||
|
||||
let mut map = Map::new();
|
||||
map.insert("wow".to_owned(), 4);
|
||||
map.insert("such".to_owned(), 5);
|
||||
assert_eq!(x, map);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tweaked_with_macro_struct() {
|
||||
// partial eq and debug because of assert_eq in this test
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
struct Wow {
|
||||
such: i32,
|
||||
field: f32,
|
||||
}
|
||||
|
||||
let tweak_path = &["tweak_test_struct", "tweak"];
|
||||
|
||||
run_with_file(tweak_path, |file| {
|
||||
file.write_all(
|
||||
br"
|
||||
(
|
||||
such: 5,
|
||||
field: 35.752346,
|
||||
)
|
||||
",
|
||||
)
|
||||
.expect("failed to write to the file");
|
||||
|
||||
let x: Wow = crate::tweak_from!(tweak_path);
|
||||
let expected = Wow {
|
||||
such: 5,
|
||||
field: 35.752_346,
|
||||
};
|
||||
assert_eq!(x, expected);
|
||||
});
|
||||
}
|
||||
|
||||
fn run_with_path(tweak_path: &[&str], test: impl Fn(&Path)) {
|
||||
let (tweak_dir, tweak_name) = directory_and_name(tweak_path);
|
||||
|
||||
let tweak_folder = ASSETS_PATH.join(tweak_dir);
|
||||
let test_path = tweak_folder.join(format!("{}.ron", tweak_name));
|
||||
|
||||
let _file_guard = FileGuard::hold(&test_path);
|
||||
|
||||
test(&test_path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tweak() {
|
||||
let tweak_path = &["tweak_create_test", "tweak"];
|
||||
|
||||
run_with_path(tweak_path, |test_path| {
|
||||
let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5);
|
||||
assert_eq!(x, 5);
|
||||
assert!(test_path.is_file());
|
||||
// Recheck it loads back correctly
|
||||
let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5);
|
||||
assert_eq!(x, 5);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tweak_deep() {
|
||||
let tweak_path = &["so_much", "deep_test", "tweak_create_test", "tweak"];
|
||||
|
||||
run_with_path(tweak_path, |test_path| {
|
||||
let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5);
|
||||
assert_eq!(x, 5);
|
||||
assert!(test_path.is_file());
|
||||
// Recheck it loads back correctly
|
||||
let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5);
|
||||
assert_eq!(x, 5);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_but_prioritize_loaded() {
|
||||
let tweak_path = &["tweak_create_and_prioritize_test", "tweak"];
|
||||
|
||||
run_with_path(tweak_path, |test_path| {
|
||||
let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5);
|
||||
assert_eq!(x, 5);
|
||||
assert!(test_path.is_file());
|
||||
|
||||
// Recheck it loads back
|
||||
// with content as priority
|
||||
fs::write(test_path, b"10").expect("failed to write to the file");
|
||||
let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5);
|
||||
assert_eq!(x, 10);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue