diff options
| author | metamuffin <metamuffin@disroot.org> | 2025-10-10 14:26:27 +0200 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2025-10-10 14:26:27 +0200 |
| commit | f35edbd85b87090b64434a6996e1623ea5e76052 (patch) | |
| tree | a85aa1e725f9921aafeb7dae9e2d184f74a8052d /server | |
| parent | 104742a23677ba7b2507f989bc02c8a03226c3b2 (diff) | |
| download | hurrycurry-f35edbd85b87090b64434a6996e1623ea5e76052.tar hurrycurry-f35edbd85b87090b64434a6996e1623ea5e76052.tar.bz2 hurrycurry-f35edbd85b87090b64434a6996e1623ea5e76052.tar.zst | |
Implement more aggressive recipe/demand filtering; fixes book in many maps
Diffstat (limited to 'server')
| -rw-r--r-- | server/data/Cargo.toml | 1 | ||||
| -rw-r--r-- | server/data/src/book/diagram_layout.rs | 5 | ||||
| -rw-r--r-- | server/data/src/book/mod.rs | 13 | ||||
| -rw-r--r-- | server/data/src/book/recipe_diagram.rs | 14 | ||||
| -rw-r--r-- | server/data/src/filter_demands.rs (renamed from server/data/src/demands.rs) | 60 | ||||
| -rw-r--r-- | server/data/src/lib.rs | 32 |
6 files changed, 78 insertions, 47 deletions
diff --git a/server/data/Cargo.toml b/server/data/Cargo.toml index 5ba266a8..65064735 100644 --- a/server/data/Cargo.toml +++ b/server/data/Cargo.toml @@ -12,6 +12,7 @@ serde = { version = "1.0.225", features = ["derive"] } shlex = "1.3.0" clap = { version = "4.5.47", features = ["derive"] } serde_yml = "0.0.12" +log = "0.4.28" [features] fast_recipes = [] diff --git a/server/data/src/book/diagram_layout.rs b/server/data/src/book/diagram_layout.rs index 0ea26a69..29240b69 100644 --- a/server/data/src/book/diagram_layout.rs +++ b/server/data/src/book/diagram_layout.rs @@ -32,6 +32,11 @@ pub struct Layout { } pub fn diagram_layout(diagram: &mut Diagram) -> Result<()> { + // seems to have problems with a single node + if diagram.nodes.len() < 2 { + return Ok(()); + } + let mut child = Command::new("dot") .arg("-Tjson") .arg("-Kdot") diff --git a/server/data/src/book/mod.rs b/server/data/src/book/mod.rs index 263e8111..a0776f41 100644 --- a/server/data/src/book/mod.rs +++ b/server/data/src/book/mod.rs @@ -23,7 +23,7 @@ use crate::{ Serverdata, book::{diagram_layout::diagram_layout, recipe_diagram::recipe_diagram}, }; -use anyhow::Result; +use anyhow::{Context, Result}; use hurrycurry_locale::trm; use hurrycurry_protocol::{ Gamedata, Message, @@ -40,9 +40,16 @@ pub fn book(data: &Gamedata, serverdata: &Serverdata) -> Result<Book> { let mut toc = Vec::new(); for (name, repr) in serverdata.recipe_groups.clone() { - let repr = repr.into_iter().collect::<Vec<_>>(); + let repr = repr + .into_iter() + .filter(|&i| data.demands.iter().any(|d| d.input == i)) + .collect::<Vec<_>>(); + if repr.is_empty() { + continue; + } + let mut diagram = recipe_diagram(data, serverdata, &repr)?; - diagram_layout(&mut diagram)?; + diagram_layout(&mut diagram).context("during layouting")?; let title = Message::Translation { id: format!("b.{name}.title"), params: vec![], diff --git a/server/data/src/book/recipe_diagram.rs b/server/data/src/book/recipe_diagram.rs index a4b9a7b1..6ec9965f 100644 --- a/server/data/src/book/recipe_diagram.rs +++ b/server/data/src/book/recipe_diagram.rs @@ -17,7 +17,7 @@ */ use crate::Serverdata; -use anyhow::Result; +use anyhow::{Result, bail}; use hurrycurry_protocol::{ Gamedata, ItemIndex, Message, Recipe, RecipeIndex, book::{Diagram, DiagramEdge, DiagramNode, NodeStyle}, @@ -44,6 +44,11 @@ pub fn recipe_diagram( let mut have = BTreeSet::<ItemIndex>::new(); let mut recipes = BTreeSet::new(); + // If an ambient item is the target it must be moved from 'need' to 'have' early + for i in &ambient_items { + have.extend(need.take(i)); + } + #[derive(PartialEq, PartialOrd, Eq, Ord)] struct GraphRecipe { index: RecipeIndex, @@ -51,7 +56,8 @@ pub fn recipe_diagram( outputs: Vec<ItemIndex>, } - while let Some(item) = need.pop_last() { + while let Some(item) = need.pop_first() { + let mut found_recipe = false; for (ri, r) in data.recipes() { if r.outputs().contains(&item) { let gr = GraphRecipe { @@ -72,8 +78,12 @@ pub fn recipe_diagram( need.extend(gr.inputs.iter().filter(|i| !have.contains(&i))); have.extend(&gr.outputs); recipes.insert(gr); + found_recipe = true; } } + if !found_recipe { + bail!("stuck at making {}", data.item_name(item)) + } } let mut diag = Diagram::default(); diff --git a/server/data/src/demands.rs b/server/data/src/filter_demands.rs index b3ad76b4..ab175d98 100644 --- a/server/data/src/demands.rs +++ b/server/data/src/filter_demands.rs @@ -16,30 +16,32 @@ */ use hurrycurry_protocol::{Demand, ItemIndex, Recipe, TileIndex}; +use log::debug; use std::collections::{HashMap, HashSet}; -pub fn generate_demands( - tiles: &HashSet<TileIndex>, - items: &HashSet<ItemIndex>, - raw_demands: &[(ItemIndex, Option<ItemIndex>, f32)], - recipes: &[Recipe], -) -> Vec<Demand> { - let recipes = recipes - .iter() - .filter(|r| r.tile().map_or(true, |t| tiles.contains(&t))) - .collect::<Vec<_>>(); +pub fn filter_demands_and_recipes( + map_tiles: &HashSet<TileIndex>, + map_items: &HashSet<ItemIndex>, + demands: &mut Vec<Demand>, + recipes: &mut Vec<Recipe>, +) { + // Remove tile-bound recipes that cant be performed + recipes.retain(|r| r.tile().map_or(true, |t| map_tiles.contains(&t))); let mut producable = HashMap::new(); - for i in items { - producable.insert(*i, 0.0); - } + // Items already in the map have no cost + producable.extend(map_items.iter().map(|i| (*i, 0.0))); loop { let prod_count = producable.len(); - for r in &recipes { - let output_count = r.outputs().iter().filter(|o| !items.contains(o)).count(); + for r in &*recipes { + let output_count = r + .outputs() + .iter() + .filter(|o| !map_items.contains(o)) + .count(); let Some(ingred_cost) = r .inputs() .iter() @@ -74,15 +76,21 @@ pub fn generate_demands( } } - raw_demands - .iter() - .filter_map(|(i, o, d)| { - producable.get(i).map(|cost| Demand { - input: *i, - output: *o, - duration: *d, - points: *cost as i64, - }) - }) - .collect() + recipes.retain(|r| { + r.inputs().iter().all(|o| producable.contains_key(o)) + && r.outputs().iter().all(|o| producable.contains_key(o)) + }); + demands.retain_mut(|d| { + if let Some(&cost) = producable.get(&d.input) { + d.points = cost as i64; + true + } else { + false + } + }); + debug!( + "{} recipes and {} demands left", + recipes.len(), + demands.len() + ) } diff --git a/server/data/src/lib.rs b/server/data/src/lib.rs index fa078643..86dec209 100644 --- a/server/data/src/lib.rs +++ b/server/data/src/lib.rs @@ -17,15 +17,15 @@ */ pub mod book; -pub mod demands; pub mod entities; +pub mod filter_demands; pub mod index; use anyhow::{Result, anyhow, bail}; use clap::Parser; -use demands::generate_demands; +use filter_demands::filter_demands_and_recipes; use hurrycurry_protocol::{ - Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex, + Demand, Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex, book::Book, glam::{IVec2, Vec2}, }; @@ -134,8 +134,8 @@ fn build_data( ) -> Result<(Gamedata, Serverdata)> { let reg = ItemTileRegistry::default(); let mut recipes = Vec::new(); + let mut demands = Vec::new(); let mut entities = Vec::new(); - let mut raw_demands = Vec::new(); let mut recipe_groups = BTreeMap::<String, BTreeSet<ItemIndex>>::new(); for mut r in recipes_in { @@ -186,11 +186,12 @@ fn build_data( outputs: [outputs.next(), outputs.next()], }); } - RecipeDeclAction::Demand => raw_demands.push(( - inputs.next().ok_or(anyhow!("demand needs inputs"))?, - outputs.next(), - r.duration.unwrap_or(10.), - )), + RecipeDeclAction::Demand => demands.push(Demand { + input: inputs.next().ok_or(anyhow!("demand needs inputs"))?, + output: outputs.next(), + duration: r.duration.unwrap_or(10.), + points: 0, // assigned later when filtering + }), } assert_eq!(inputs.next(), None, "{r2:?} inputs left over"); assert_eq!(outputs.next(), None, "{r2:?} outputs left over"); @@ -257,17 +258,20 @@ fn build_data( } } + let chef_spawn = chef_spawn.ok_or(anyhow!("map has no chef spawn"))?; + for tile in tile_specs.values() { if !tiles_used.contains(®.register_tile(tile.tile_name.clone())) { bail!("tile {:?} is unused", tile.tile_name) } } - let chef_spawn = chef_spawn.ok_or(anyhow!("map has no chef spawn"))?; - entities.extend(map_in.entities.clone()); + for e in &entities { + e.run_register(®); + } - let demands = generate_demands(&tiles_used, &items_used, &raw_demands, &recipes); + filter_demands_and_recipes(&tiles_used, &items_used, &mut demands, &mut recipes); let mut maps = maps .iter() @@ -295,10 +299,6 @@ fn build_data( } } - for e in &entities { - e.run_register(®); - } - let item_names = reg.items.into_inner().unwrap(); let tile_names = reg.tiles.into_inner().unwrap(); |