diff --git a/.gitlab/CI/check.gitlab-ci.yml b/.gitlab/CI/check.gitlab-ci.yml index 60f0e8425f..51f306d8e8 100644 --- a/.gitlab/CI/check.gitlab-ci.yml +++ b/.gitlab/CI/check.gitlab-ci.yml @@ -6,7 +6,7 @@ code-quality: script: - ln -s /dockercache/target target - rm -r target/debug/incremental/* || echo "all good" # TMP FIX FOR 2021-03-22-nightly - - cargo clippy --all-targets --locked --features="bin_csv,bin_bot,asset_tweak" -- -D warnings + - cargo clippy --all-targets --locked --features="bin_csv,bin_graphviz,bin_bot,asset_tweak" -- -D warnings - cargo fmt --all -- --check security: diff --git a/Cargo.lock b/Cargo.lock index 145b1f4fee..d6f4c1a325 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5796,6 +5796,7 @@ dependencies = [ "num-derive", "num-traits", "ordered-float 2.5.1", + "petgraph 0.5.1", "rand 0.8.3", "rayon", "ron", diff --git a/common/Cargo.toml b/common/Cargo.toml index 0d9f72e846..7a58d607c5 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -9,6 +9,7 @@ no-assets = [] tracy = ["common-base/tracy"] simd = ["vek/platform_intrinsics"] bin_csv = ["ron", "csv", "structopt"] +bin_graphviz = ["petgraph"] default = ["simd"] @@ -51,6 +52,8 @@ ron = { version = "0.6", default-features = false, optional = true } # csv export csv = { version = "1.1.3", optional = true } structopt = { version = "0.3.13", optional = true } +# graphviz exporters +petgraph = { version = "0.5.1", optional = true } # Data structures hashbrown = { version = "0.11", features = ["rayon", "serde", "nightly"] } @@ -88,3 +91,7 @@ required-features = ["bin_csv"] [[bin]] name = "csv_import" required-features = ["bin_csv"] + +[[bin]] +name = "recipe_graphviz" +required-features = ["bin_graphviz"] diff --git a/common/src/bin/recipe_graphviz.rs b/common/src/bin/recipe_graphviz.rs new file mode 100644 index 0000000000..b7fb176207 --- /dev/null +++ b/common/src/bin/recipe_graphviz.rs @@ -0,0 +1,59 @@ +use hashbrown::HashMap; +use petgraph::{ + dot::{Config, Dot}, + Graph, +}; +use std::{fs::File, io::Write}; +use veloren_common::{ + assets::AssetExt, + comp::item::ItemDesc, + recipe::{RecipeBook, RecipeInput}, +}; + +fn main() { + let recipes = RecipeBook::load_expect_cloned("common.recipe_book"); + let mut graph = Graph::new(); + let mut nodes = HashMap::new(); + let mut add_node = |graph: &mut Graph<_, _>, node: &str| { + *nodes + .entry(node.to_owned()) + .or_insert_with(|| graph.add_node(node.to_owned())) + }; + for (_, recipe) in recipes.iter() { + let output = recipe.output.0.item_definition_id(); + let inputs = recipe + .inputs + .iter() + .map(|(i, _)| i) + .filter_map(|input| { + if let RecipeInput::Item(item) = input { + Some(item.item_definition_id()) + } else { + None + } + }) + .collect::>(); + let out_node = add_node(&mut graph, output); + for input in inputs.iter() { + let in_node = add_node(&mut graph, input); + graph.add_edge(in_node, out_node, ()); + } + } + // you can render the dot file as a png with `dot -Tpng recipe_graph.dot > + // recipe_graph.png` or interactively view it with `xdot recipe_graph.dot` + let mut f = File::create("recipe_graph.dot").unwrap(); + writeln!(f, "digraph {{").unwrap(); + writeln!(f, "rankdir = \"LR\"").unwrap(); + writeln!( + f, + "{:#?}", + Dot::with_attr_getters( + &graph, + &[Config::EdgeNoLabel, Config::GraphContentOnly], + &|_, _| "".to_owned(), + &|_, _| { "constraint=false".to_owned() } + ) + ) + .unwrap(); + writeln!(f, "}}").unwrap(); +}