aboutsummaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-10-10 14:26:27 +0200
committermetamuffin <metamuffin@disroot.org>2025-10-10 14:26:27 +0200
commitf35edbd85b87090b64434a6996e1623ea5e76052 (patch)
treea85aa1e725f9921aafeb7dae9e2d184f74a8052d /server
parent104742a23677ba7b2507f989bc02c8a03226c3b2 (diff)
downloadhurrycurry-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.toml1
-rw-r--r--server/data/src/book/diagram_layout.rs5
-rw-r--r--server/data/src/book/mod.rs13
-rw-r--r--server/data/src/book/recipe_diagram.rs14
-rw-r--r--server/data/src/filter_demands.rs (renamed from server/data/src/demands.rs)60
-rw-r--r--server/data/src/lib.rs32
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(&reg.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(&reg);
+ }
- 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(&reg);
- }
-
let item_names = reg.items.into_inner().unwrap();
let tile_names = reg.tiles.into_inner().unwrap();