From 176e6bc6c4c29bea3be2aceca99743b997c76c97 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 6 Oct 2025 23:03:32 +0200 Subject: Move data code to own crate + general data refactor --- server/tools/src/book.rs | 123 ------------------- server/tools/src/diagram_layout.rs | 78 ------------ server/tools/src/graph.rs | 4 +- server/tools/src/graph_summary.rs | 5 +- server/tools/src/main.rs | 23 ++-- server/tools/src/map_linter.rs | 4 +- server/tools/src/recipe_diagram.rs | 239 ------------------------------------- 7 files changed, 17 insertions(+), 459 deletions(-) delete mode 100644 server/tools/src/book.rs delete mode 100644 server/tools/src/diagram_layout.rs delete mode 100644 server/tools/src/recipe_diagram.rs (limited to 'server/tools/src') diff --git a/server/tools/src/book.rs b/server/tools/src/book.rs deleted file mode 100644 index bffbe836..00000000 --- a/server/tools/src/book.rs +++ /dev/null @@ -1,123 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright (C) 2025 Hurry Curry! Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -*/ - -use crate::{diagram_layout::diagram_layout, recipe_diagram::recipe_diagram}; -use anyhow::Result; -use hurrycurry_locale::trm; -use hurrycurry_protocol::{ - Gamedata, Message, - book::{Book, BookPage}, -}; -use hurrycurry_server::data::Serverdata; - -struct RecipePageParams<'a> { - name: &'a str, - repr_items: &'a [&'a str], -} -static RECIPE_PAGES: &[RecipePageParams] = &[ - RecipePageParams { - name: "cheese-leek-soup", - repr_items: &["plate:cheese-leek-soup"], - }, - RecipePageParams { - name: "tomato-soup", - repr_items: &["plate:tomato-soup"], - }, - RecipePageParams { - name: "mushroom-soup", - repr_items: &["plate:mushroom-soup"], - }, - RecipePageParams { - name: "burger", - repr_items: &[ - "plate:seared-patty,sliced-bun,sliced-lettuce,sliced-tomato", - "plate:seared-patty,sliced-bun,sliced-cheese,sliced-tomato", - ], - }, - RecipePageParams { - name: "noodles", - repr_items: &["plate:cooked-noodles,sliced-cheese,tomato-juice"], - }, - RecipePageParams { - name: "pizza", - repr_items: &["plate:baked-rolled-dough:sliced-cheese,sliced-mushroom,tomato-juice"], - }, - RecipePageParams { - name: "curry", - repr_items: &["plate:cooked-rice,curry"], - }, - RecipePageParams { - name: "drinks", - repr_items: &[ - "glass:water", - "glass:tomato-juice", - "glass:strawberry-shake", - ], - }, - RecipePageParams { - name: "mochi", - repr_items: &["plate:strawberry-mochi"], - }, - RecipePageParams { - name: "doughnut", - repr_items: &["plate:doughnut"], - }, - RecipePageParams { - name: "doughnut", - repr_items: &["plate:doughnut"], - }, -]; - -pub fn book(data: &Gamedata, serverdata: &Serverdata) -> Result { - let mut pages = Vec::new(); - - pages.push(BookPage::Contents { - title: trm!("b.toc.title"), - table: vec![], - }); - let mut toc = Vec::new(); - - for &RecipePageParams { name, repr_items } in RECIPE_PAGES { - let mut diagram = recipe_diagram(data, serverdata, repr_items)?; - diagram_layout(&mut diagram)?; - let title = Message::Translation { - id: format!("b.{name}.title"), - params: vec![], - }; - toc.push((title.clone(), pages.len())); - pages.push(BookPage::Recipe { - title, - description: Message::Translation { - id: format!("b.{name}.desc"), - params: vec![], - }, - diagram, - }); - } - - if let BookPage::Contents { table, .. } = &mut pages[0] { - *table = toc; - } - Ok(Book { pages }) -} - -pub fn print_book(data: &Gamedata, serverdata: &Serverdata) -> Result<()> { - let book = book(data, serverdata)?; - println!("{}", serde_json::to_string_pretty(&book).unwrap()); - Ok(()) -} diff --git a/server/tools/src/diagram_layout.rs b/server/tools/src/diagram_layout.rs deleted file mode 100644 index 0ea26a69..00000000 --- a/server/tools/src/diagram_layout.rs +++ /dev/null @@ -1,78 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright (C) 2025 Hurry Curry! Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -*/ - -use anyhow::{Result, anyhow}; -use hurrycurry_protocol::{book::Diagram, glam::Vec2}; -use serde::Deserialize; -use std::{ - collections::BTreeMap, - fmt::Write as W2, - io::Write, - process::{Command, Stdio}, -}; - -pub struct Layout { - pub nodes: BTreeMap, - pub edges: Vec<(usize, usize)>, -} - -pub fn diagram_layout(diagram: &mut Diagram) -> Result<()> { - let mut child = Command::new("dot") - .arg("-Tjson") - .arg("-Kdot") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; - - let mut out = String::new(); - writeln!(out, "digraph {{")?; - for (i, _) in diagram.nodes.iter().enumerate() { - writeln!(out, "k{i} [width=1, height=1]")?; - } - for edge in &diagram.edges { - writeln!(out, "k{} -> k{}", edge.src, edge.dst)?; - } - writeln!(out, "}}")?; - - child.stdin.as_mut().unwrap().write_all(out.as_bytes())?; - let output = child.wait_with_output()?; - - #[derive(Deserialize)] - struct Out { - objects: Vec, - } - #[derive(Deserialize)] - struct Object { - name: String, - pos: String, - } - let graph: Out = serde_json::from_slice(&output.stdout)?; - for o in graph.objects { - let pos = o.pos.split_once(",").ok_or(anyhow!("malformed position"))?; - let pos = Vec2::new(pos.0.parse()?, pos.1.parse()?); - - let index = o - .name - .strip_prefix("k") - .ok_or(anyhow!("invalid node name"))? - .parse::()?; - diagram.nodes[index].position = pos - } - - Ok(()) -} diff --git a/server/tools/src/graph.rs b/server/tools/src/graph.rs index 53f70d99..a65ffc97 100644 --- a/server/tools/src/graph.rs +++ b/server/tools/src/graph.rs @@ -16,8 +16,8 @@ */ use anyhow::Result; +use hurrycurry_data::index::DataIndex; use hurrycurry_protocol::{Demand, ItemIndex, Recipe, RecipeIndex}; -use hurrycurry_server::data::DataIndex; pub(crate) fn graph() -> Result<()> { let mut index = DataIndex::default(); @@ -25,7 +25,7 @@ pub(crate) fn graph() -> Result<()> { println!("digraph {{"); - let (data, _, _) = index.generate("5star")?; + let (data, _) = index.generate("5star")?; for i in 0..data.item_names.len() { println!("i{i} [label=\"{}\"]", data.item_name(ItemIndex(i))) } diff --git a/server/tools/src/graph_summary.rs b/server/tools/src/graph_summary.rs index be53e768..bfdcc955 100644 --- a/server/tools/src/graph_summary.rs +++ b/server/tools/src/graph_summary.rs @@ -15,9 +15,10 @@ along with this program. If not, see . */ + use anyhow::Result; +use hurrycurry_data::index::DataIndex; use hurrycurry_protocol::{ItemIndex, Recipe, TileIndex}; -use hurrycurry_server::data::DataIndex; use std::collections::HashSet; pub(crate) fn graph_summary() -> Result<()> { @@ -26,7 +27,7 @@ pub(crate) fn graph_summary() -> Result<()> { println!("digraph {{"); - let (data, sdata, _) = index.generate("5star")?; + let (data, sdata) = index.generate("5star")?; struct Node { inputs: Vec, diff --git a/server/tools/src/main.rs b/server/tools/src/main.rs index b550dabb..f70c5755 100644 --- a/server/tools/src/main.rs +++ b/server/tools/src/main.rs @@ -16,31 +16,28 @@ */ -pub mod book; pub mod book_html; pub mod diagram_dot; -pub mod diagram_layout; pub mod diagram_svg; pub mod graph; pub mod graph_summary; pub mod map_linter; -pub mod recipe_diagram; use crate::{ - book::{book, print_book}, book_html::render_html_book, diagram_dot::{diagram_dot, diagram_dot_svg}, - diagram_layout::diagram_layout, diagram_svg::diagram_svg, graph::graph, graph_summary::graph_summary, map_linter::check_map, - recipe_diagram::recipe_diagram, }; use anyhow::Result; use clap::Parser; +use hurrycurry_data::{ + book::{book, diagram_layout::diagram_layout, print_book, recipe_diagram::recipe_diagram}, + index::DataIndex, +}; use hurrycurry_locale::FALLBACK_LOCALE; -use hurrycurry_server::data::DataIndex; #[derive(Parser)] enum Action { @@ -83,7 +80,7 @@ fn main() -> Result<()> { } => { let mut index = DataIndex::default(); index.reload()?; - let (data, serverdata, _) = index.generate("5star")?; + let (data, serverdata) = index.generate("5star")?; let mut diagram = recipe_diagram(&data, &serverdata, &[&out])?; let out = if dot_out { diagram_dot(&data, &diagram, false)? @@ -99,20 +96,20 @@ fn main() -> Result<()> { Action::Book => { let mut index = DataIndex::default(); index.reload()?; - let (data, serverdata, _) = index.generate("5star")?; + let (data, serverdata) = index.generate("5star")?; print_book(&data, &serverdata)? } Action::BookHtml => { let mut index = DataIndex::default(); index.reload()?; - let (data, serverdata, _) = index.generate("5star")?; + let (data, serverdata) = index.generate("5star")?; let book = book(&data, &serverdata)?; println!("{}", render_html_book(&data, &book, &FALLBACK_LOCALE)); } Action::MapDemands { map } => { let mut index = DataIndex::default(); index.reload()?; - let (data, _, _) = index.generate(&map)?; + let (data, _) = index.generate(&map)?; for demand in &data.demands { println!("{}", data.item_name(demand.input)) } @@ -120,7 +117,7 @@ fn main() -> Result<()> { Action::MapItems { map } => { let mut index = DataIndex::default(); index.reload()?; - let (data, _, _) = index.generate(&map)?; + let (data, _) = index.generate(&map)?; for name in &data.item_names { println!("{name}") } @@ -128,7 +125,7 @@ fn main() -> Result<()> { Action::MapTiles { map } => { let mut index = DataIndex::default(); index.reload()?; - let (data, _, _) = index.generate(&map)?; + let (data, _) = index.generate(&map)?; for name in &data.tile_names { println!("{name}") } diff --git a/server/tools/src/map_linter.rs b/server/tools/src/map_linter.rs index 738a9e10..678f2930 100644 --- a/server/tools/src/map_linter.rs +++ b/server/tools/src/map_linter.rs @@ -17,6 +17,7 @@ */ use anyhow::Result; +use hurrycurry_data::{Serverdata, index::DataIndex}; use hurrycurry_locale::{ FALLBACK_LOCALE, message::{COLORED, MessageDisplayExt}, @@ -26,7 +27,6 @@ use hurrycurry_protocol::{ Gamedata, TileIndex, glam::{IVec2, ivec2}, }; -use hurrycurry_server::data::{DataIndex, Serverdata}; use std::{ collections::{BTreeSet, HashMap, HashSet}, sync::LazyLock, @@ -148,7 +148,7 @@ pub fn check_map(map: &str) -> Result<()> { let locale = &*FALLBACK_LOCALE; let mut index = DataIndex::default(); index.reload()?; - let (data, serverdata, _) = index.generate(map)?; + let (data, serverdata) = index.generate(map)?; let mut warnings = Vec::new(); diff --git a/server/tools/src/recipe_diagram.rs b/server/tools/src/recipe_diagram.rs deleted file mode 100644 index 0be75433..00000000 --- a/server/tools/src/recipe_diagram.rs +++ /dev/null @@ -1,239 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright (C) 2025 Hurry Curry! Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -*/ - -use anyhow::Result; -use hurrycurry_protocol::{ - Gamedata, ItemIndex, Message, Recipe, RecipeIndex, - book::{Diagram, DiagramEdge, DiagramNode, NodeStyle}, - glam::Vec2, -}; -use hurrycurry_server::data::Serverdata; -use std::{ - cmp::Reverse, - collections::{BTreeMap, BTreeSet, HashSet}, -}; - -pub(crate) fn recipe_diagram( - data: &Gamedata, - serverdata: &Serverdata, - target_items: &[&str], -) -> Result { - let ambient_items = serverdata - .initial_map - .iter() - .flat_map(|(_, (_, item))| item) - .copied() - .collect::>(); - - let mut need = BTreeSet::from_iter( - target_items - .iter() - .map(|name| data.get_item_by_name(name).unwrap()), - ); - let mut have = BTreeSet::::new(); - let mut recipes = BTreeSet::new(); - - #[derive(PartialEq, PartialOrd, Eq, Ord)] - struct GraphRecipe { - index: RecipeIndex, - inputs: Vec, - outputs: Vec, - } - - while let Some(item) = need.pop_last() { - for (ri, r) in data.recipes() { - if r.outputs().contains(&item) { - let gr = GraphRecipe { - inputs: r - .inputs() - .iter() - .filter(|i| !ambient_items.contains(i)) - .copied() - .collect(), - outputs: r - .outputs() - .iter() - .filter(|i| !ambient_items.contains(i)) - .copied() - .collect(), - index: ri, - }; - need.extend(gr.inputs.iter().filter(|i| !have.contains(&i))); - have.extend(&gr.outputs); - recipes.insert(gr); - } - } - } - - let mut diag = Diagram::default(); - let mut item_index = BTreeMap::new(); - for i in have { - item_index.insert(i, diag.nodes.len()); - diag.nodes.push(DiagramNode { - label: Message::Item(i), - position: Vec2::ZERO, - style: if target_items.contains(&data.item_name(i)) { - NodeStyle::FinalProduct - } else { - NodeStyle::IntermediateProduct - }, - }); - } - for r in recipes { - let index = diag.nodes.len(); - let recipe = data.recipe(r.index); - - if matches!(recipe, Recipe::Instant { .. }) && r.inputs.len() >= 1 && r.outputs.len() >= 1 { - for i in r.inputs { - diag.edges.push(DiagramEdge { - src: item_index[&i], - dst: item_index[&r.outputs[0]], - label: None, - }); - } - continue; - } - if matches!(recipe, Recipe::Passive { tile: None, .. }) - && r.inputs.len() == 1 - && r.outputs.len() == 1 - { - diag.nodes[item_index[&r.inputs[0]]].style = NodeStyle::ProcessPassive; - diag.edges.push(DiagramEdge { - src: item_index[&r.inputs[0]], - dst: item_index[&r.outputs[0]], - label: None, - }); - continue; - } - - let (kind, style) = match recipe { - Recipe::Passive { .. } => ("Passive", NodeStyle::ProcessPassive), - Recipe::Active { .. } => ("Active", NodeStyle::ProcessActive), - Recipe::Instant { .. } => ("Instant", NodeStyle::ProcessInstant), - }; - diag.nodes.push(DiagramNode { - position: Vec2::ZERO, - label: if let Some(tile) = recipe.tile() { - Message::Tile(tile) - } else { - Message::Text(kind.to_string()) - }, - style, - }); - - for i in r.inputs { - diag.edges.push(DiagramEdge { - src: item_index[&i], - dst: index, - label: None, - }); - } - for o in r.outputs { - diag.edges.push(DiagramEdge { - src: index, - dst: item_index[&o], - label: None, - }); - } - } - - merge_combine_clusters(&mut diag); - remove_orphan_nodes(&mut diag); - - Ok(diag) -} - -fn merge_combine_clusters(diag: &mut Diagram) { - let instant_nodes = diag - .nodes - .iter() - .enumerate() - .filter(|(_, n)| matches!(n.style, NodeStyle::IntermediateProduct)) - .map(|(i, _)| i) - .collect::>(); - - for ni in instant_nodes { - let inputs = diag - .edges - .iter() - .enumerate() - .filter(|(_, e)| e.dst == ni) - .map(|(i, e)| (i, e.src)) - .collect::>(); - let outputs = diag - .edges - .iter() - .enumerate() - .filter(|(_, e)| e.src == ni) - .map(|(i, e)| (i, e.dst)) - .collect::>(); - - if outputs - .iter() - .all(|&(_, ni)| diag.nodes[ni].style.is_procuct()) - && inputs - .iter() - .all(|&(_, ni)| diag.nodes[ni].style.is_procuct()) - { - let mut to_remove = inputs - .iter() - .map(|&(i, _)| i) - .chain(outputs.iter().map(|&(i, _)| i)) - .collect::>(); - to_remove.sort_by_key(|x| Reverse(*x)); - for i in to_remove { - diag.edges.remove(i); - } - - for &input in &inputs { - for &output in &outputs { - if !diag - .edges - .iter() - .any(|e| e.src == input.1 && e.dst == output.1) - { - diag.edges.push(DiagramEdge { - src: input.1, - dst: output.1, - label: None, - }); - } - } - } - } - } -} - -fn remove_orphan_nodes(diag: &mut Diagram) { - let mut to_remove = diag - .nodes - .iter() - .enumerate() - .filter(|&(i, _)| !diag.edges.iter().any(|e| e.src == i || e.dst == i)) - .map(|(i, _)| i) - .collect::>(); - to_remove.sort_by_key(|x| Reverse(*x)); - - for e in &mut diag.edges { - e.src -= to_remove.iter().filter(|&&i| i < e.src).count(); - e.dst -= to_remove.iter().filter(|&&i| i < e.dst).count(); - } - for i in to_remove { - diag.nodes.remove(i); - } -} -- cgit v1.2.3-70-g09d2