aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-10-06 23:03:32 +0200
committermetamuffin <metamuffin@disroot.org>2025-10-06 23:03:40 +0200
commit176e6bc6c4c29bea3be2aceca99743b997c76c97 (patch)
tree1161e7a966843324756340da4b6452492902fa07
parentea86b11b682500160f37b35ea8f06b081cd05036 (diff)
downloadhurrycurry-176e6bc6c4c29bea3be2aceca99743b997c76c97.tar
hurrycurry-176e6bc6c4c29bea3be2aceca99743b997c76c97.tar.bz2
hurrycurry-176e6bc6c4c29bea3be2aceca99743b997c76c97.tar.zst
Move data code to own crate + general data refactor
-rw-r--r--Cargo.lock17
-rw-r--r--Cargo.toml1
-rw-r--r--data/makefile4
-rw-r--r--data/recipes/default.js104
-rw-r--r--server/Cargo.toml4
-rw-r--r--server/data/Cargo.toml17
-rw-r--r--server/data/src/book/diagram_layout.rs (renamed from server/tools/src/diagram_layout.rs)0
-rw-r--r--server/data/src/book/mod.rs (renamed from server/tools/src/book.rs)9
-rw-r--r--server/data/src/book/recipe_diagram.rs (renamed from server/tools/src/recipe_diagram.rs)4
-rw-r--r--server/data/src/demands.rs (renamed from server/src/data/demands.rs)0
-rw-r--r--server/data/src/entities.rs99
-rw-r--r--server/data/src/index.rs94
-rw-r--r--server/data/src/lib.rs (renamed from server/src/data/mod.rs)139
-rw-r--r--server/src/entity/campaign.rs28
-rw-r--r--server/src/entity/conveyor.rs23
-rw-r--r--server/src/entity/customers.rs6
-rw-r--r--server/src/entity/environment_effect.rs14
-rw-r--r--server/src/entity/mod.rs157
-rw-r--r--server/src/lib.rs1
-rw-r--r--server/src/main.rs6
-rw-r--r--server/src/server.rs13
-rw-r--r--server/tools/Cargo.toml1
-rw-r--r--server/tools/src/graph.rs4
-rw-r--r--server/tools/src/graph_summary.rs5
-rw-r--r--server/tools/src/main.rs23
-rw-r--r--server/tools/src/map_linter.rs4
26 files changed, 379 insertions, 398 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2d166b39..50cf378b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -997,6 +997,20 @@ dependencies = [
]
[[package]]
+name = "hurrycurry-data"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "hurrycurry-locale",
+ "hurrycurry-protocol",
+ "serde",
+ "serde_json",
+ "serde_yml",
+ "shlex",
+]
+
+[[package]]
name = "hurrycurry-discover"
version = "2.3.5"
dependencies = [
@@ -1098,6 +1112,7 @@ dependencies = [
"get_if_addrs",
"hurrycurry-bot",
"hurrycurry-client-lib",
+ "hurrycurry-data",
"hurrycurry-locale",
"hurrycurry-protocol",
"igd",
@@ -1109,7 +1124,6 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
- "serde_yml",
"shlex",
"tokio",
"tokio-tungstenite",
@@ -1123,6 +1137,7 @@ dependencies = [
"anyhow",
"clap",
"env_logger",
+ "hurrycurry-data",
"hurrycurry-locale",
"hurrycurry-protocol",
"hurrycurry-server",
diff --git a/Cargo.toml b/Cargo.toml
index 887ac828..7c4252db 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ members = [
"server/replaytool",
"server/registry",
"server/discover",
+ "server/data",
"server/editor",
"server/tools",
"locale/tools",
diff --git a/data/makefile b/data/makefile
index c9361258..104fc876 100644
--- a/data/makefile
+++ b/data/makefile
@@ -14,7 +14,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
SETS = default none anticurry
-ALL = $(patsubst %,recipes/%.yaml,$(SETS)) book.json
+ALL = $(patsubst %,recipes/%.yaml,$(SETS))
all: $(ALL)
graphs: recipes/default.svg
@@ -29,8 +29,6 @@ recipes/default.yaml: recipes/default.js
recipes/%.gv.txt: recipes/%.yaml
{ pushd .. >/dev/null; cargo $(CARGOFLAGS) run --release --bin hurrycurry-tools -- graph; popd >/dev/null; } > $@~ && cp $@~ $@
-book.json:
- { pushd .. >/dev/null; cargo $(CARGOFLAGS) run --release --bin hurrycurry-tools -- book; popd >/dev/null; } > $@~ && cp $@~ $@
recipes/%.svg: recipes/%.gv.txt
dot -Tsvg -Kdot < $< > $@~ && cp $@~ $@
diff --git a/data/recipes/default.js b/data/recipes/default.js
index 22f4f037..6bcfb315 100644
--- a/data/recipes/default.js
+++ b/data/recipes/default.js
@@ -45,6 +45,8 @@ function finish() {
s += ` inputs: [${r.inputs.map(e => JSON.stringify(e.toString())).join(",")}]\n`
s += ` outputs: [${r.outputs.map(e => JSON.stringify(e.toString())).join(",")}]\n`
if (r.warn) s += ` warn: true\n`
+ if (r.group) s += ` group: ${r.group}\n`
+ if (r.inputs[0]?.group_hidden) s += ` group_hidden: true\n`
if (r.duration) s += ` duration: ${r.duration}\n`
if (r.revert_duration) s += ` revert_duration: ${r.revert_duration}\n`
if (r.points) s += ` points: ${r.points}\n`
@@ -96,6 +98,11 @@ function auto_burn() {
class Item {
constructor(name, container) { this.name = name; this.container = container }
as(s) { this.name = s; return this }
+ tr_nested(container) {
+ const o = new Item(this.toString(), container)
+ out({ action: "instant", inputs: [container, this], outputs: [o] })
+ return o
+ }
tr(container) {
const o = new Item(this.name, container)
if (this.container == container) return o
@@ -151,7 +158,9 @@ function sear(s, duration = 15) {
return o
}
function bake(s, duration = 25) {
- const o = new Item(`baked-${s.name}`, s.container)
+ const o = s.container == "rolled-dough"
+ ? new Item(s.name, `baked-${s.container}`)
+ : new Item(`baked-${s.name}`, s.container)
out({ action: "passive", duration, revert_duration: duration * 2, tile: "oven", inputs: [s], outputs: [o] })
out({ action: "passive", duration: duration / 2, revert_duration: duration / 4, tile: "oven", inputs: [o], outputs: [new Item("burned")], warn: true })
return o
@@ -201,48 +210,9 @@ function combine(c, ...items) {
}
return result
}
-// function combine(container, ...items) {
-// items.push(container)
-// const open = items.map(i => [i.name])
-// const seen = new Set()
-// let final_result
-// let components_before
-// while ((components_before = open.pop())) {
-// for (const new_item of items) {
-// if (components_before.includes(new_item.name)) continue
-
-// // generate key `old,old,old#new` to avoid duplicated recipes
-// const dedup_key = components_before.join(",") + "#" + new_item
-// if (seen.has(dedup_key)) continue
-// seen.add(dedup_key)
-
-// if (new_item.container && !components_before.includes(container.name))
-// continue // new item is likely a liquid and needs target container to be there already
-
-// const components_after = [...components_before, new_item.name]
-// components_after.sort()
-// open.push(components_after)
-
-// const content_in = components_before.filter(e => e != container.name).join(",")
-// const item_in = content_in == "" ? container : new Item(content_in, components_before.includes(container.name) ? container : null)
-// const content_out = components_after.filter(e => e != container.name).join(",")
-// const item_out = content_out == "" ? container : new Item(content_out, components_after.includes(container.name) ? container : null)
-
-// if (components_after.length == items.length) final_result = item_out
-// out({
-// action: "instant",
-// inputs: [item_in, new_item],
-// outputs: new_item == container // put result in containers positions if container was added
-// ? [null, item_out] : [item_out, new_item.container]
-// })
-// }
-// }
-// console.error(final_result);
-// return final_result
-// }
-function edible(...items) {
+function edible(group, ...items) {
for (const i of items) {
- out({ action: "demand", inputs: [i], outputs: [i.container?.dispose ?? i.container], duration: 10 })
+ out({ action: "demand", inputs: [i], outputs: [i.container?.dispose ?? i.container], duration: 10, group })
}
}
function either(a, b) {
@@ -255,8 +225,12 @@ function sink_fill(c) {
out({ action: "active", inputs: [c], outputs: [o], tile: "sink", duration: 1 })
return o
}
+function group_hidden(i) {
+ i.group_hidden = true
+ return i
+}
-out({ action: "active", duration: 2, tile: "sink", inputs: [new Item("dirty-plate")], outputs: [PL] })
+out({ group: "cleanup", action: "active", duration: 2, tile: "sink", inputs: [new Item("dirty-plate")], outputs: [PL] })
const tomato = crate("tomato")
const steak = crate("steak")
@@ -278,9 +252,9 @@ const dough = process(flour.tr(FP)).as("dough").tr()
// Pizza
const pizza_dough = roll(dough)
-edible(
- bake(combine(pizza_dough, tomato_juice, cut(cheese))).tr(PL),
- bake(combine(pizza_dough, tomato_juice, cut(cheese), cut(mushroom))).tr(PL),
+edible("pizza",
+ bake(combine(pizza_dough, tomato_juice, cut(cheese), cut(mushroom))).tr_nested(PL),
+ group_hidden(bake(combine(pizza_dough, tomato_juice, cut(cheese))).tr_nested(PL))
// bake(combine(pizza_dough, patty, cut(cheese))).tr(PL),
// bake(combine(pizza_dough, patty, leek)).tr(PL),
)
@@ -291,9 +265,9 @@ edible(
// Steak
const french_fries = deep_fry(cut(potato).tr(BA)).as("french-fries");
const bun = either(bake(dough).as("bun"), crate("bun"))
-edible(
- bun.tr(PL),
- french_fries.tr(PL),
+edible("steak",
+ group_hidden(bun.tr(PL)),
+ group_hidden(french_fries.tr(PL)),
combine(PL, sear(steak), bun),
combine(PL, sear(steak), french_fries),
// combine(PL, sear(steak), cook(potato.tr(POT))),
@@ -303,32 +277,32 @@ edible(
)
// Salad
-edible(
+edible("salad",
combine(PL, cut(tomato), cut(lettuce)),
combine(PL, cut(lettuce)),
)
// Burger
-edible(
- combine(PL, cut(bun), sear(patty), cut(cheese)),
- combine(PL, cut(bun), sear(patty), cut(cheese), cut(lettuce)),
+edible("burger",
combine(PL, cut(bun), sear(patty), cut(tomato), cut(lettuce)),
combine(PL, cut(bun), sear(patty), cut(cheese), cut(tomato)),
- combine(PL, cut(bun), cut(cheese), cut(lettuce), cut(tomato)),
- combine(PL, cut(bun), cut(lettuce), cut(tomato)),
- combine(PL, cut(bun), cut(fish))
+ group_hidden(combine(PL, cut(bun), sear(patty), cut(cheese))),
+ group_hidden(combine(PL, cut(bun), sear(patty), cut(cheese), cut(lettuce))),
+ group_hidden(combine(PL, cut(bun), cut(cheese), cut(lettuce), cut(tomato))),
+ group_hidden(combine(PL, cut(bun), cut(lettuce), cut(tomato))),
+ group_hidden(combine(PL, cut(bun), cut(fish)))
)
// Noodles
const noodle = cook(cut(roll(dough)).as("noodles").tr(POT))
-edible(
+edible("noodles",
combine(PL, noodle, tomato_juice),
combine(PL, noodle, tomato_juice, cut(cheese)),
// combine(PL, noodle, tomato_juice, cut(cheese), sear(patty)),
)
// Soup
-edible(
+edible("soups",
cook(combine(POT, tomato_juice)).as("tomato-soup").tr(PL),
cook(combine(POT, cut(mushroom))).as("mushroom-soup").tr(PL),
cook(combine(POT, cheese, cut(leek))).as("cheese-leek-soup").tr(PL),
@@ -336,9 +310,9 @@ edible(
)
// Rice and nigiri
-edible(
+edible("nigiri",
container_add(cut(fish), cook(rice.tr(POT))).as("nigiri").tr(PL),
- container_add(cook(rice.tr(POT).tr(PL)), cut(fish)).as("nigiri")
+ container_add(cook(rice.tr(POT)).tr(PL), cut(fish)).as("nigiri"),
)
// coconut milk and strawberry puree
@@ -350,19 +324,19 @@ const strawberry_shake = either(
)
// Doughnut (Pfannkuchen)
-edible(deep_fry(dough.tr(BA)).as("doughnut").tr(PL))
+edible("doughnut", deep_fry(dough.tr(BA)).as("doughnut").tr(PL))
// Icecream
-edible(freeze(strawberry_shake.tr(GL)).as("strawberry-icecream"))
+edible("icecream", freeze(strawberry_shake.tr(GL)).as("strawberry-icecream"))
// Mochi
const rice_flour = process(rice.tr(FP)).as("rice-flour")
const mochi_dough = cook(rice_flour.tr(POT), 5).as("mochi-dough")
const strawberry_mochi = container_add(strawberry, mochi_dough).as("strawberry-mochi")
-edible(strawberry_mochi.tr(PL))
+edible("mochi", strawberry_mochi.tr(PL))
// Drinks
-edible(
+edible("drinks",
strawberry_shake.tr(GL),
tomato_juice.tr(GL),
sink_fill(GL)
@@ -370,7 +344,7 @@ edible(
// Curry
const curry_with_rice = combine(PL, cook(rice.tr(POT)), cook(combine(POT, milk, tomato, leek)).as("curry"))
-edible(curry_with_rice)
+edible("curry", curry_with_rice)
auto_trash()
auto_burn()
diff --git a/server/Cargo.toml b/server/Cargo.toml
index e14bb84d..f72cb7fa 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -13,7 +13,6 @@ tokio = { version = "1.47.1", features = ["full"] }
serde_json = "1.0.145"
tokio-tungstenite = "0.27.0"
futures-util = "0.3.31"
-serde_yml = "0.0.12"
rand = "0.9.2"
rand_distr = "0.5.1"
shlex = "1.3.0"
@@ -34,6 +33,7 @@ hurrycurry-locale = { path = "locale" }
hurrycurry-protocol = { path = "protocol" }
hurrycurry-client-lib = { path = "client-lib" }
hurrycurry-bot = { path = "bot" }
+hurrycurry-data = { path = "data" }
[target.'cfg(windows)'.dependencies]
windows-registry = "0.6"
@@ -44,4 +44,4 @@ mdns = ["dep:mdns-sd", "dep:get_if_addrs"]
register = ["dep:reqwest"]
upnp = ["dep:igd", "dep:get_if_addrs"]
-fast_recipes = []
+fast_recipes = ["hurrycurry-data/fast_recipes"]
diff --git a/server/data/Cargo.toml b/server/data/Cargo.toml
new file mode 100644
index 00000000..5ba266a8
--- /dev/null
+++ b/server/data/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "hurrycurry-data"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+hurrycurry-protocol = { path = "../protocol" }
+hurrycurry-locale = { path = "../locale" }
+anyhow = "1.0.100"
+serde_json = "1.0.145"
+serde = { version = "1.0.225", features = ["derive"] }
+shlex = "1.3.0"
+clap = { version = "4.5.47", features = ["derive"] }
+serde_yml = "0.0.12"
+
+[features]
+fast_recipes = []
diff --git a/server/tools/src/diagram_layout.rs b/server/data/src/book/diagram_layout.rs
index 0ea26a69..0ea26a69 100644
--- a/server/tools/src/diagram_layout.rs
+++ b/server/data/src/book/diagram_layout.rs
diff --git a/server/tools/src/book.rs b/server/data/src/book/mod.rs
index bffbe836..b52779f3 100644
--- a/server/tools/src/book.rs
+++ b/server/data/src/book/mod.rs
@@ -16,14 +16,19 @@
*/
-use crate::{diagram_layout::diagram_layout, recipe_diagram::recipe_diagram};
+pub mod diagram_layout;
+pub mod recipe_diagram;
+
+use crate::{
+ Serverdata,
+ book::{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,
diff --git a/server/tools/src/recipe_diagram.rs b/server/data/src/book/recipe_diagram.rs
index 0be75433..2ec92b68 100644
--- a/server/tools/src/recipe_diagram.rs
+++ b/server/data/src/book/recipe_diagram.rs
@@ -16,19 +16,19 @@
*/
+use crate::Serverdata;
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(
+pub fn recipe_diagram(
data: &Gamedata,
serverdata: &Serverdata,
target_items: &[&str],
diff --git a/server/src/data/demands.rs b/server/data/src/demands.rs
index 77e187af..77e187af 100644
--- a/server/src/data/demands.rs
+++ b/server/data/src/demands.rs
diff --git a/server/data/src/entities.rs b/server/data/src/entities.rs
new file mode 100644
index 00000000..68dbe479
--- /dev/null
+++ b/server/data/src/entities.rs
@@ -0,0 +1,99 @@
+/*
+ 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 <https://www.gnu.org/licenses/>.
+
+*/
+
+use hurrycurry_protocol::glam::{IVec2, Vec2};
+use serde::{Deserialize, Serialize};
+
+use crate::ItemTileRegistry;
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum EntityDecl {
+ Conveyor {
+ from: IVec2,
+ to: IVec2,
+ speed: Option<f32>,
+ },
+ ItemPortal {
+ from: IVec2,
+ to: IVec2,
+ },
+ PlayerPortal {
+ from: Vec2,
+ to: Vec2,
+ },
+ Customers {
+ scaling_factor: Option<f32>,
+ },
+ Map {
+ name: String,
+ pos: Vec2,
+ },
+ EnvironmentEffect(EnvironmentEffect),
+ Environment(Vec<String>),
+ Gate {
+ condition: GateCondition,
+ pos: IVec2,
+ },
+ Tram {
+ length: usize,
+ color: Option<i32>,
+ points: Vec<Vec2>,
+ spacing: f32,
+ smoothing: f32,
+ },
+ Book {
+ pos: IVec2,
+ },
+ Pedestrians {
+ spawn_delay: f32,
+ spawn_delay_stdev: Option<f32>,
+ speed: Option<f32>,
+ points: Vec<Vec2>,
+ },
+}
+
+impl EntityDecl {
+ pub(crate) fn run_register(&self, reg: &ItemTileRegistry) {
+ match self {
+ Self::Gate { .. } => drop(reg.register_tile("fence".into())),
+ Self::Customers { .. } => drop(reg.register_item("unknown-order".into())),
+ _ => (),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum GateCondition {
+ All(Vec<GateCondition>),
+ Any(Vec<GateCondition>),
+ Stars(String, u8),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize, Default)]
+pub struct EnvironmentEffect {
+ pub name: String,
+ #[serde(default = "default_onoff")]
+ pub on: f32,
+ #[serde(default = "default_onoff")]
+ pub off: f32,
+}
+fn default_onoff() -> f32 {
+ 40.
+}
diff --git a/server/data/src/index.rs b/server/data/src/index.rs
new file mode 100644
index 00000000..a5ec8d97
--- /dev/null
+++ b/server/data/src/index.rs
@@ -0,0 +1,94 @@
+/*
+ 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 <https://www.gnu.org/licenses/>.
+
+*/
+
+use anyhow::{Context, Result, anyhow, bail};
+use hurrycurry_protocol::{Gamedata, MapMetadata};
+use serde::Deserialize;
+use std::{
+ collections::{HashMap, HashSet},
+ fs::{File, read_to_string},
+ path::PathBuf,
+ str::FromStr,
+ sync::Mutex,
+};
+
+use crate::{MapDecl, Serverdata, book::book, build_data};
+
+#[derive(Debug, Deserialize, Default)]
+pub struct DataIndex {
+ pub maps: HashMap<String, MapMetadata>,
+ pub recipes: HashSet<String>,
+}
+
+pub static DATA_DIR: Mutex<Option<PathBuf>> = Mutex::new(None);
+fn data_dir() -> PathBuf {
+ DATA_DIR
+ .lock()
+ .unwrap()
+ .to_owned()
+ .unwrap_or_else(|| PathBuf::from_str("data").unwrap())
+}
+
+impl DataIndex {
+ pub fn load() -> Result<Self> {
+ let mut s = Self::default();
+ s.reload()?;
+ Ok(s)
+ }
+ pub fn reload(&mut self) -> Result<()> {
+ *self = serde_yml::from_reader(File::open(data_dir().join("index.yaml"))?)?;
+ Ok(())
+ }
+
+ pub fn read_map(&self, name: &str) -> Result<String> {
+ // Scary!
+ if name.contains("..") || name.starts_with("/") || name.contains("//") {
+ bail!("illegal map path");
+ }
+ let path = data_dir().join(format!("maps/{name}.yaml"));
+ Ok(read_to_string(path)?)
+ }
+ pub fn read_recipes(&self, name: &str) -> Result<String> {
+ if !self.recipes.contains(name) {
+ bail!("unknown recipes: {name:?}");
+ }
+ let path = data_dir().join(format!("recipes/{name}.yaml"));
+ Ok(read_to_string(path)?)
+ }
+ pub fn generate(&self, map: &str) -> Result<(Gamedata, Serverdata)> {
+ let map_in: MapDecl = serde_yml::from_str(
+ &self
+ .read_map(map)
+ .context(anyhow!("Failed to read map file ({map})"))?,
+ )
+ .context(anyhow!("Failed to parse map file ({map})"))?;
+ let recipes_in = serde_yml::from_str(
+ &self
+ .read_recipes(map_in.recipes.as_deref().unwrap_or("default"))
+ .context("Failed read recipe file")?,
+ )
+ .context("Failed to parse recipe file")?;
+
+ build_data(&self.maps, map.to_string(), map_in, recipes_in)
+ }
+ pub fn generate_with_book(&self, map: &str) -> Result<(Gamedata, Serverdata)> {
+ let (gd, mut sd) = self.generate(map)?;
+ sd.book = book(&gd, &sd).context("within book")?;
+ Ok((gd, sd))
+ }
+}
diff --git a/server/src/data/mod.rs b/server/data/src/lib.rs
index 74fae62c..822d6997 100644
--- a/server/src/data/mod.rs
+++ b/server/data/src/lib.rs
@@ -15,28 +15,29 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+
+pub mod book;
pub mod demands;
+pub mod entities;
+pub mod index;
-use crate::entity::{construct_entity, Entities, EntityDecl};
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{Result, anyhow, bail};
use clap::Parser;
use demands::generate_demands;
-use hurrycurry_bot::algos::ALGO_CONSTRUCTORS;
use hurrycurry_protocol::{
+ Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex,
book::Book,
glam::{IVec2, Vec2},
- Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex,
};
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, HashMap, HashSet},
- fs::{read_to_string, File},
- path::PathBuf,
- str::FromStr,
- sync::{Mutex, RwLock},
+ sync::RwLock,
time::Duration,
};
+use crate::entities::EntityDecl;
+
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default)]
#[serde(rename_all = "snake_case")]
pub enum RecipeDeclAction {
@@ -113,6 +114,7 @@ pub struct Serverdata {
pub default_timer: Option<Duration>,
pub book: Book,
pub flags: ServerdataFlags,
+ pub entity_decls: Vec<EntityDecl>
}
#[rustfmt::skip]
@@ -121,82 +123,12 @@ pub struct ServerdataFlags {
#[serde(default)] pub disable_unknown_orders: bool,
}
-#[derive(Debug, Deserialize, Default)]
-pub struct DataIndex {
- pub maps: HashMap<String, MapMetadata>,
- pub recipes: HashSet<String>,
-}
-
-pub static DATA_DIR: Mutex<Option<PathBuf>> = Mutex::new(None);
-fn data_dir() -> PathBuf {
- DATA_DIR
- .lock()
- .unwrap()
- .to_owned()
- .unwrap_or_else(|| PathBuf::from_str("data").unwrap())
-}
-
-impl DataIndex {
- pub fn load() -> Result<Self> {
- let mut s = Self::default();
- s.reload()?;
- Ok(s)
- }
- pub fn reload(&mut self) -> Result<()> {
- *self = serde_yml::from_reader(File::open(data_dir().join("index.yaml"))?)?;
- Ok(())
- }
-
- pub fn read_map(&self, name: &str) -> Result<String> {
- // Scary!
- if name.contains("..") || name.starts_with("/") || name.contains("//") {
- bail!("illegal map path");
- }
- let path = data_dir().join(format!("maps/{name}.yaml"));
- Ok(read_to_string(path)?)
- }
- pub fn read_recipes(&self, name: &str) -> Result<String> {
- if !self.recipes.contains(name) {
- bail!("unknown recipes: {name:?}");
- }
- let path = data_dir().join(format!("recipes/{name}.yaml"));
- Ok(read_to_string(path)?)
- }
- pub fn generate(&self, map: &str) -> Result<(Gamedata, Serverdata, Entities)> {
- let map_in: MapDecl = serde_yml::from_str(
- &self
- .read_map(map)
- .context(anyhow!("Failed to read map file ({map})"))?,
- )
- .context(anyhow!("Failed to parse map file ({map})"))?;
- let recipes_in = serde_yml::from_str(
- &self
- .read_recipes(map_in.recipes.as_deref().unwrap_or("default"))
- .context("Failed read recipe file")?,
- )
- .context("Failed to parse recipe file")?;
-
- build_data(&self.maps, map.to_string(), map_in, recipes_in)
- }
- pub fn generate_with_book(&self, map: &str) -> Result<(Gamedata, Serverdata, Entities)> {
- let (gd, mut sd, es) = self.generate(map)?;
- sd.book = self.read_book()?;
- Ok((gd, sd, es))
- }
- pub fn read_book(&self) -> Result<Book> {
- serde_json::from_str(
- &read_to_string(data_dir().join("book.json")).context("Failed to read book file")?,
- )
- .context("Failed to parse book file")
- }
-}
-
-pub fn build_data(
+fn build_data(
maps: &HashMap<String, MapMetadata>,
map_name: String,
map_in: MapDecl,
recipes_in: Vec<RecipeDecl>,
-) -> Result<(Gamedata, Serverdata, Entities)> {
+) -> Result<(Gamedata, Serverdata)> {
let reg = ItemTileRegistry::default();
let mut recipes = Vec::new();
let mut entities = Vec::new();
@@ -300,25 +232,18 @@ pub fn build_data(
exclusive_tiles.entry(tile).or_default().extend(item);
}
if tile_spec.book {
- entities.push(construct_entity(Some(pos), &EntityDecl::Book, &reg)?);
+ entities.push(EntityDecl::Book { pos });
}
if let Some(off) = &tile_spec.conveyor {
let (x, y) = off
.split_once(",")
.ok_or(anyhow!("conveyor offset invalid format"))?;
let dir = IVec2::new(x.parse()?, y.parse()?);
- entities.push(construct_entity(
- Some(pos),
- &EntityDecl::Conveyor {
- dir: Some(dir),
- filter: None,
- filter_dir: None,
- from: None,
- speed: None,
- to: None,
- },
- &reg,
- )?);
+ entities.push(EntityDecl::Conveyor {
+ from: pos,
+ speed: None,
+ to: pos + dir,
+ });
}
}
}
@@ -331,21 +256,10 @@ pub fn build_data(
let chef_spawn = chef_spawn.ok_or(anyhow!("map has no chef spawn"))?;
- entities.extend(
- map_in
- .entities
- .iter()
- .map(|decl| construct_entity(None, decl, &reg))
- .try_collect::<Vec<_>>()?,
- );
+ entities.extend(map_in.entities.clone());
let demands = generate_demands(&tiles_used, &items_used, &raw_demands, &recipes);
- let bot_algos = ALGO_CONSTRUCTORS
- .iter()
- .map(|(name, _)| (*name).to_owned())
- .collect::<Vec<String>>();
-
let mut maps = maps
.iter()
.filter(|(_, v)| v.players > 0)
@@ -372,6 +286,10 @@ pub 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();
@@ -383,7 +301,6 @@ pub fn build_data(
Ok((
Gamedata {
- bot_algos,
current_map: map_name,
maps,
tile_walkable,
@@ -393,6 +310,12 @@ pub fn build_data(
item_names,
demands,
tile_names,
+ bot_algos: vec![
+ "waiter".to_string(),
+ "simple".to_string(),
+ "dishwasher".to_string(),
+ "frank".to_string(),
+ ],
hand_count: map_in.hand_count.unwrap_or(1),
},
Serverdata {
@@ -403,13 +326,13 @@ pub fn build_data(
default_timer,
book: Book::default(),
score_baseline: map_in.score_baseline,
+ entity_decls: entities,
},
- entities,
))
}
#[derive(Default)]
-pub struct ItemTileRegistry {
+pub(crate) struct ItemTileRegistry {
tiles: RwLock<Vec<String>>,
items: RwLock<Vec<String>>,
}
diff --git a/server/src/entity/campaign.rs b/server/src/entity/campaign.rs
index 53ea6582..fdc169d1 100644
--- a/server/src/entity/campaign.rs
+++ b/server/src/entity/campaign.rs
@@ -18,16 +18,16 @@
use super::{Entity, EntityContext};
use crate::{scoreboard::ScoreboardStore, server::GameServerExt};
use anyhow::Result;
+use hurrycurry_data::entities::GateCondition;
use hurrycurry_locale::{trm, TrError};
use hurrycurry_protocol::{
glam::{IVec2, Vec2},
Message, PacketC, PlayerID, TileIndex,
};
-use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone)]
pub struct Map {
- pub location: Vec2,
+ pub pos: Vec2,
pub name: String,
}
@@ -36,7 +36,7 @@ impl Entity for Map {
let mut activate = false;
c.game
.players_spatial_index
- .query(self.location, 0.5, |_, _| activate = true);
+ .query(self.pos, 0.5, |_, _| activate = true);
if activate {
*c.load_map = Some(self.name.clone());
@@ -46,19 +46,11 @@ impl Entity for Map {
}
}
-#[derive(Debug, Clone, Deserialize, Serialize)]
-#[serde(rename_all = "kebab-case")]
-pub enum GateCondition {
- All(Vec<GateCondition>),
- Any(Vec<GateCondition>),
- Stars(String, u8),
-}
-
#[derive(Debug, Clone)]
pub struct Gate {
pub active: bool,
pub unlocked: bool,
- pub location: IVec2,
+ pub pos: IVec2,
pub blocker_tile: TileIndex,
pub condition: GateCondition,
}
@@ -69,7 +61,7 @@ impl Entity for Gate {
self.unlocked = self.condition.check(c.scoreboard);
if !self.unlocked {
c.game
- .set_tile(self.location, Some(self.blocker_tile), c.packet_out);
+ .set_tile(self.pos, Some(self.blocker_tile), c.packet_out);
c.packet_out.push_back(PacketC::FlushMap); // TODO dont send too often
}
}
@@ -81,7 +73,7 @@ impl Entity for Gate {
pos: Option<IVec2>,
_player: PlayerID,
) -> Result<bool, TrError> {
- if !self.unlocked && pos == Some(self.location) {
+ if !self.unlocked && pos == Some(self.pos) {
c.packet_out.push_back(PacketC::ServerMessage {
message: trm!(
"s.campaign.unlock_condition",
@@ -95,7 +87,11 @@ impl Entity for Gate {
}
}
-impl GateCondition {
+trait GateConditionExt {
+ fn check(&self, scoreboard: &ScoreboardStore) -> bool;
+ fn show(&self, scoreboard: &ScoreboardStore) -> Message;
+}
+impl GateConditionExt for GateCondition {
fn check(&self, scoreboard: &ScoreboardStore) -> bool {
match self {
GateCondition::All(cs) => cs.iter().all(|c| c.check(scoreboard)),
@@ -105,7 +101,7 @@ impl GateCondition {
.is_some_and(|s| s.best.first().is_some_and(|b| b.score.stars >= *thres)),
}
}
- pub fn show(&self, scoreboard: &ScoreboardStore) -> Message {
+ fn show(&self, scoreboard: &ScoreboardStore) -> Message {
match self {
GateCondition::All(cs) => cs
.iter()
diff --git a/server/src/entity/conveyor.rs b/server/src/entity/conveyor.rs
index 9c7f5e4d..e31410e3 100644
--- a/server/src/entity/conveyor.rs
+++ b/server/src/entity/conveyor.rs
@@ -18,14 +18,12 @@
use super::{Entity, EntityContext};
use crate::interaction::interact;
use anyhow::{anyhow, bail, Result};
-use hurrycurry_protocol::{glam::IVec2, ItemIndex, ItemLocation};
+use hurrycurry_protocol::{glam::IVec2, ItemLocation};
#[derive(Debug, Clone)]
pub struct Conveyor {
pub(super) from: IVec2,
pub(super) to: IVec2,
- pub(super) filter_tile: Option<IVec2>,
- pub(super) filter_item: Option<ItemIndex>,
pub(super) cooldown: f32,
pub(super) max_cooldown: f32,
}
@@ -38,24 +36,7 @@ impl Entity for Conveyor {
.get(&self.from)
.ok_or(anyhow!("conveyor from missing"))?;
- if let Some(from_item) = from.item.as_ref() {
- let filter = if let Some(t) = &self.filter_tile {
- let filter_tile = c
- .game
- .tiles
- .get(t)
- .ok_or(anyhow!("conveyor filter missing"))?;
- filter_tile.item.as_ref().map(|e| e.kind)
- } else {
- self.filter_item.as_ref().map(|i| *i)
- };
-
- if let Some(filter) = filter {
- if from_item.kind != filter {
- return Ok(());
- }
- }
-
+ if from.item.is_some() {
self.cooldown += c.dt;
if self.cooldown < self.max_cooldown {
return Ok(());
diff --git a/server/src/entity/customers.rs b/server/src/entity/customers.rs
index e3a23830..22522e4e 100644
--- a/server/src/entity/customers.rs
+++ b/server/src/entity/customers.rs
@@ -29,13 +29,13 @@ pub struct Customers {
}
impl Customers {
- pub fn new(scaling_factor: f32) -> Result<Self> {
- Ok(Self {
+ pub fn new(scaling_factor: f32) -> Self {
+ Self {
customers: Default::default(),
spawn_cooldown: 0.,
chair_count: None,
scaling_factor,
- })
+ }
}
}
diff --git a/server/src/entity/environment_effect.rs b/server/src/entity/environment_effect.rs
index 95040954..ba2c395e 100644
--- a/server/src/entity/environment_effect.rs
+++ b/server/src/entity/environment_effect.rs
@@ -16,23 +16,11 @@
*/
use super::{Entity, EntityContext};
+use hurrycurry_data::entities::EnvironmentEffect;
use hurrycurry_protocol::PacketC;
use rand::random;
-use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
-#[derive(Clone, Debug, Deserialize, Serialize, Default)]
-pub struct EnvironmentEffect {
- name: String,
- #[serde(default = "default_onoff")]
- on: f32,
- #[serde(default = "default_onoff")]
- off: f32,
-}
-fn default_onoff() -> f32 {
- 40.
-}
-
#[derive(Clone, Debug)]
pub struct EnvironmentEffectController {
config: EnvironmentEffect,
diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs
index 928910bc..47d37f3d 100644
--- a/server/src/entity/mod.rs
+++ b/server/src/entity/mod.rs
@@ -27,26 +27,19 @@ pub mod player_portal;
pub mod tram;
pub mod tutorial;
-use crate::{
- data::{ItemTileRegistry, Serverdata},
- entity::pedestrians::Pedestrians,
- scoreboard::ScoreboardStore,
-};
-use anyhow::{anyhow, Result};
+use crate::{entity::pedestrians::Pedestrians, scoreboard::ScoreboardStore};
+use anyhow::Result;
use book::Book;
-use campaign::{Gate, GateCondition, Map};
+use campaign::{Gate, Map};
use conveyor::Conveyor;
use customers::Customers;
-use environment_effect::{EnvironmentController, EnvironmentEffect, EnvironmentEffectController};
+use environment_effect::{EnvironmentController, EnvironmentEffectController};
use hurrycurry_client_lib::Game;
+use hurrycurry_data::{entities::EntityDecl, Serverdata};
use hurrycurry_locale::TrError;
-use hurrycurry_protocol::{
- glam::{IVec2, Vec2},
- Character, PacketC, PacketS, PlayerID,
-};
+use hurrycurry_protocol::{glam::IVec2, Character, Gamedata, PacketC, PacketS, PlayerID};
use item_portal::ItemPortal;
use player_portal::PlayerPortal;
-use serde::{Deserialize, Serialize};
use std::{
any::Any,
collections::{HashMap, VecDeque},
@@ -87,135 +80,29 @@ pub trait Entity: Any {
}
}
-// macro_rules! entities {
-// ($($e:ident),*) => {
-// pub enum DynEntity { $($e($e)),* }
-// impl Entity for DynEntity {
-// fn tick(&mut self, c: EntityContext<'_>) -> Result<()> {
-// match self { $(DynEntity::$e(x) => x.tick(c)),*, }
-// }
-// fn destructor(&mut self, c: EntityContext<'_>) {
-// match self { $(DynEntity::$e(x) => x.destructor(c)),*, }
-// }
-// }
-// };
-// }
-// entities!(
-// Conveyor,
-// ItemPortal,
-// PlayerPortal,
-// Customers,
-// EnvironmentEffectController,
-// EnvironmentController
-// );
-
-#[derive(Debug, Clone, Deserialize, Serialize)]
-#[serde(rename_all = "snake_case")]
-pub enum EntityDecl {
- Conveyor {
- from: Option<IVec2>,
- to: Option<IVec2>,
- filter_dir: Option<IVec2>,
- filter: Option<String>,
- dir: Option<IVec2>,
- speed: Option<f32>,
- },
- ItemPortal {
- from: Option<IVec2>,
- to: IVec2,
- },
- PlayerPortal {
- from: Option<Vec2>,
- to: Vec2,
- },
- Customers {
- scaling_factor: Option<f32>,
- },
- Map {
- name: String,
- location: Option<Vec2>,
- },
- EnvironmentEffect(EnvironmentEffect),
- Environment(Vec<String>),
- Gate {
- location: Option<IVec2>,
- condition: GateCondition,
- },
- Tram {
- length: usize,
- color: Option<i32>,
- points: Vec<Vec2>,
- spacing: f32,
- smoothing: f32,
- },
- Book,
- Pedestrians {
- spawn_delay: f32,
- spawn_delay_stdev: Option<f32>,
- speed: Option<f32>,
- points: Vec<Vec2>,
- },
-}
-
-pub fn construct_entity(
- pos: Option<IVec2>,
- decl: &EntityDecl,
- reg: &ItemTileRegistry,
-) -> Result<DynEntity> {
- Ok(match decl.to_owned() {
- EntityDecl::Book => Box::new(Book(pos.ok_or(anyhow!("book is tile entity"))?)),
- EntityDecl::ItemPortal { from, to } => Box::new(ItemPortal {
- from: from
- .or(pos)
- .ok_or(anyhow!("Item portal start without start"))?,
- to,
- }),
- EntityDecl::PlayerPortal { from, to } => Box::new(PlayerPortal {
- from: from
- .or(pos.map(|v| v.as_vec2()))
- .ok_or(anyhow!("Player portal without start"))?,
- to,
- }),
- EntityDecl::Conveyor {
+pub fn construct_entity(decl: &EntityDecl, data: &Gamedata) -> DynEntity {
+ match decl.to_owned() {
+ EntityDecl::Book { pos } => Box::new(Book(pos)),
+ EntityDecl::ItemPortal { from, to } => Box::new(ItemPortal { from, to }),
+ EntityDecl::PlayerPortal { from, to } => Box::new(PlayerPortal { from, to }),
+ EntityDecl::Conveyor { from, to, speed } => Box::new(Conveyor {
from,
to,
- speed,
- dir,
- filter,
- filter_dir,
- } => {
- let from = from.or(pos).ok_or(anyhow!("Conveyor has no start"))?;
- let to = to
- .or(dir.map(|s| s + from))
- .ok_or(anyhow!("Conveyor has no destination"))?;
- Box::new(Conveyor {
- from,
- to,
- max_cooldown: 1. / speed.unwrap_or(2.),
- filter_tile: filter_dir.map(|o| to + o),
- filter_item: filter.map(|name| reg.register_item(name)),
- cooldown: 0.,
- })
- }
- EntityDecl::Map { name, location } => Box::new(Map {
- location: location
- .or(pos.map(|p| p.as_vec2() + 0.5))
- .ok_or(anyhow!("no location"))?,
- name,
+ max_cooldown: 1. / speed.unwrap_or(2.),
+ cooldown: 0.,
}),
- EntityDecl::Gate {
- condition,
- location,
- } => Box::new(Gate {
+ EntityDecl::Map { name, pos } => Box::new(Map { pos, name }),
+ EntityDecl::Gate { condition, pos } => Box::new(Gate {
condition,
unlocked: false,
- location: location.or(pos).ok_or(anyhow!("no location"))?,
- blocker_tile: reg.register_tile("fence".to_string()),
+ pos,
+ blocker_tile: data
+ .get_tile_by_name("fence")
+ .expect("asserted earlier (tm)"),
active: true,
}),
EntityDecl::Customers { scaling_factor } => {
- reg.register_item("unknown-order".to_owned());
- Box::new(Customers::new(scaling_factor.unwrap_or(0.5))?)
+ Box::new(Customers::new(scaling_factor.unwrap_or(0.5)))
}
EntityDecl::EnvironmentEffect(config) => Box::new(EnvironmentEffectController::new(config)),
EntityDecl::Environment(names) => Box::new(EnvironmentController(names)),
@@ -254,5 +141,5 @@ pub fn construct_entity(
cooldown: 0.,
speed: speed.unwrap_or(0.6),
}),
- })
+ }
}
diff --git a/server/src/lib.rs b/server/src/lib.rs
index 3e01ba36..da49f85d 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -17,7 +17,6 @@
*/
#![feature(if_let_guard, let_chains, iterator_try_collect, stmt_expr_attributes)]
pub mod commands;
-pub mod data;
pub mod entity;
pub mod interaction;
pub mod network;
diff --git a/server/src/main.rs b/server/src/main.rs
index 5514e7d0..b1265420 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -18,9 +18,10 @@
use anyhow::{bail, Result};
use clap::Parser;
use futures_util::{SinkExt, StreamExt};
+use hurrycurry_data::index::DATA_DIR;
use hurrycurry_locale::trm;
use hurrycurry_protocol::{PacketC, PacketS};
-use hurrycurry_server::{data::DATA_DIR, server::Server, ConnectionID};
+use hurrycurry_server::{server::Server, ConnectionID};
use log::{debug, info, trace, warn, LevelFilter};
use std::{
env::var, net::SocketAddr, path::PathBuf, process::exit, str::FromStr, sync::Arc,
@@ -314,8 +315,9 @@ async fn run(args: Args) -> anyhow::Result<()> {
#[cfg(test)]
mod test {
+ use hurrycurry_data::index::DATA_DIR;
use hurrycurry_protocol::{Character, PacketS, PlayerClass, PlayerID};
- use hurrycurry_server::{data::DATA_DIR, server::Server, ConnectionID};
+ use hurrycurry_server::{server::Server, ConnectionID};
use std::future::Future;
use tokio::sync::broadcast;
diff --git a/server/src/server.rs b/server/src/server.rs
index e16fdb61..c370f3c4 100644
--- a/server/src/server.rs
+++ b/server/src/server.rs
@@ -16,14 +16,14 @@
*/
use crate::{
- data::{DataIndex, Serverdata},
- entity::{Entities, EntityContext},
+ entity::{construct_entity, Entities, EntityContext},
interaction::{interact, tick_slot},
scoreboard::ScoreboardStore,
ConnectionID,
};
use anyhow::{Context, Result};
use hurrycurry_client_lib::{gamedata_index::GamedataIndex, Game, Involvement, Item, Player, Tile};
+use hurrycurry_data::{index::DataIndex, Serverdata};
use hurrycurry_locale::{tre, TrError};
use hurrycurry_protocol::{
glam::{IVec2, Vec2},
@@ -353,7 +353,7 @@ impl Server {
impl Server {
pub fn load(
&mut self,
- (gamedata, serverdata, entities): (Gamedata, Serverdata, Entities),
+ (gamedata, serverdata): (Gamedata, Serverdata),
timer: Option<Duration>,
) {
for mut e in self.entities.drain(..) {
@@ -369,7 +369,7 @@ impl Server {
load_map: &mut None,
});
}
- self.tick(0.);
+ self.tick(0.); // TODO ?
self.game.load(
gamedata,
&serverdata,
@@ -377,8 +377,11 @@ impl Server {
&mut self.packet_out,
);
self.gamedata_index.update(&self.game.data);
+ for ed in &serverdata.entity_decls {
+ self.entities.push(construct_entity(ed, &self.game.data));
+ }
self.data = serverdata.into();
- self.entities = entities;
+ self.entities.clear();
for e in &mut self.entities {
e.constructor(EntityContext {
game: &mut self.game,
diff --git a/server/tools/Cargo.toml b/server/tools/Cargo.toml
index db1c5ebf..1c427c81 100644
--- a/server/tools/Cargo.toml
+++ b/server/tools/Cargo.toml
@@ -11,6 +11,7 @@ clap = { version = "4.5.47", features = ["derive"] }
hurrycurry-protocol = { path = "../protocol" }
hurrycurry-server = { path = ".." }
hurrycurry-locale = { path = "../locale" }
+hurrycurry-data = { path = "../data" }
serde_json = "1.0.145"
serde = { version = "1.0.225", features = ["derive"] }
markup = "0.15.0"
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 <https://www.gnu.org/licenses/>.
*/
+
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<ItemIndex>,
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();