aboutsummaryrefslogtreecommitdiff
path: root/server/src/data.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-06-25 23:34:10 +0200
committermetamuffin <metamuffin@disroot.org>2024-06-25 23:34:10 +0200
commit4dc15a1e86ef1ae985fdf36f1a84d07b1de99ea7 (patch)
tree6a262cd2be9abee480adda3e367c7f8abf8845d6 /server/src/data.rs
parent84c90e84a1e0d6cd2eae36fd8888354b4e23c354 (diff)
downloadhurrycurry-4dc15a1e86ef1ae985fdf36f1a84d07b1de99ea7.tar
hurrycurry-4dc15a1e86ef1ae985fdf36f1a84d07b1de99ea7.tar.bz2
hurrycurry-4dc15a1e86ef1ae985fdf36f1a84d07b1de99ea7.tar.zst
server can change map at runtime
Diffstat (limited to 'server/src/data.rs')
-rw-r--r--server/src/data.rs233
1 files changed, 144 insertions, 89 deletions
diff --git a/server/src/data.rs b/server/src/data.rs
index 64509f37..e980ccbd 100644
--- a/server/src/data.rs
+++ b/server/src/data.rs
@@ -19,9 +19,14 @@ use crate::{
interaction::Recipe,
protocol::{DemandIndex, ItemIndex, RecipeIndex, TileIndex},
};
+use anyhow::{anyhow, bail, Result};
use glam::{IVec2, Vec2};
use serde::{Deserialize, Serialize};
-use std::{collections::HashMap, sync::RwLock};
+use std::{
+ collections::{HashMap, HashSet},
+ fs::File,
+ sync::RwLock,
+};
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default)]
#[serde(rename_all = "snake_case")]
@@ -78,7 +83,9 @@ pub struct Demand {
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Gamedata {
- recipes: Vec<Recipe>,
+ #[serde(skip)]
+ pub recipes: Vec<Recipe>,
+ #[serde(skip)]
pub demands: Vec<Demand>,
pub item_names: Vec<String>,
pub tile_names: Vec<String>,
@@ -90,105 +97,153 @@ pub struct Gamedata {
pub customer_spawn: Vec2,
}
-pub fn build_gamedata(
- recipes_in: Vec<RecipeDecl>,
- map_in: InitialMap,
- demands_in: Vec<DemandDecl>,
-) -> Gamedata {
- let item_names = RwLock::new(Vec::new());
- let tile_names = RwLock::new(Vec::new());
- let mut recipes = Vec::new();
- let mut demands = Vec::new();
+#[derive(Debug, Deserialize, Default)]
+pub struct DataIndex {
+ pub maps: HashSet<String>,
+ pub demands: HashSet<String>,
+ pub recipes: HashSet<String>,
+}
+
+impl DataIndex {
+ pub fn reload(&mut self) -> anyhow::Result<()> {
+ *self = serde_yaml::from_reader(File::open("data/index.yaml")?)?;
+ Ok(())
+ }
+
+ pub fn generate(&self, spec: String) -> anyhow::Result<Gamedata> {
+ let [demands, map, recipes] = spec
+ .split("-")
+ .collect::<Vec<_>>()
+ .try_into()
+ .map_err(|_| anyhow!("data specification malformed"))?;
+
+ if !self.demands.contains(demands) {
+ bail!("unknown demands: {demands:?}");
+ }
+ if !self.maps.contains(map) {
+ bail!("unknown map: {map:?}");
+ }
+ if !self.recipes.contains(recipes) {
+ bail!("unknown recipes: {recipes:?}");
+ }
+
+ let demands_path = format!("data/demands/{demands}.yaml");
+ let map_path = format!("data/maps/{map}.yaml");
+ let recipes_path = format!("data/recipes/{recipes}.yaml");
+
+ let demands_in = serde_yaml::from_reader(File::open(demands_path).unwrap()).unwrap();
+ let map_in = serde_yaml::from_reader(File::open(map_path).unwrap()).unwrap();
+ let recipes_in = serde_yaml::from_reader(File::open(recipes_path).unwrap()).unwrap();
+
+ Ok(Gamedata::build(recipes_in, map_in, demands_in)?)
+ }
+}
+
+impl Gamedata {
+ pub fn build(
+ recipes_in: Vec<RecipeDecl>,
+ map_in: InitialMap,
+ demands_in: Vec<DemandDecl>,
+ ) -> Result<Self> {
+ let item_names = RwLock::new(Vec::new());
+ let tile_names = RwLock::new(Vec::new());
+ let mut recipes = Vec::new();
+ let mut demands = Vec::new();
- for r in recipes_in {
- let r2 = r.clone();
- let mut inputs = r
- .inputs
- .into_iter()
- .map(|i| ItemIndex(register(&item_names, i)));
- let mut outputs = r
- .outputs
- .into_iter()
- .map(|o| ItemIndex(register(&item_names, o)));
- let tile = r.tile.map(|t| TileIndex(register(&tile_names, t)));
- match r.action {
- Action::Never => {}
- Action::Passive => recipes.push(Recipe::Passive {
- duration: r.duration.expect("duration for passive missing"),
- warn: r.warn,
- tile,
- revert_duration: r.revert_duration,
- input: inputs.next().expect("passive recipe without input"),
- output: outputs.next(),
- }),
- Action::Active => recipes.push(Recipe::Active {
- duration: r.duration.expect("duration for active missing"),
- tile,
- input: inputs.next().expect("active recipe without input"),
- outputs: [outputs.next(), outputs.next()],
- }),
- Action::Instant => {
- recipes.push(Recipe::Instant {
+ for r in recipes_in {
+ let r2 = r.clone();
+ let mut inputs = r
+ .inputs
+ .into_iter()
+ .map(|i| ItemIndex(register(&item_names, i)));
+ let mut outputs = r
+ .outputs
+ .into_iter()
+ .map(|o| ItemIndex(register(&item_names, o)));
+ let tile = r.tile.map(|t| TileIndex(register(&tile_names, t)));
+ match r.action {
+ Action::Never => {}
+ Action::Passive => recipes.push(Recipe::Passive {
+ duration: r.duration.expect("duration for passive missing"),
+ warn: r.warn,
tile,
- inputs: [inputs.next(), inputs.next()],
+ revert_duration: r.revert_duration,
+ input: inputs.next().expect("passive recipe without input"),
+ output: outputs.next(),
+ }),
+ Action::Active => recipes.push(Recipe::Active {
+ duration: r.duration.expect("duration for active missing"),
+ tile,
+ input: inputs.next().expect("active recipe without input"),
outputs: [outputs.next(), outputs.next()],
- });
+ }),
+ Action::Instant => {
+ recipes.push(Recipe::Instant {
+ tile,
+ inputs: [inputs.next(), inputs.next()],
+ outputs: [outputs.next(), outputs.next()],
+ });
+ }
}
+ assert_eq!(inputs.next(), None, "{r2:?}");
+ assert_eq!(outputs.next(), None, "{r2:?}");
}
- assert_eq!(inputs.next(), None, "{r2:?}");
- assert_eq!(outputs.next(), None, "{r2:?}");
- }
- for d in demands_in {
- demands.push(Demand {
- from: ItemIndex(register(&item_names, d.from)),
- to: ItemIndex(register(&item_names, d.to)),
- duration: d.duration,
- })
- }
+ for d in demands_in {
+ demands.push(Demand {
+ from: ItemIndex(register(&item_names, d.from)),
+ to: ItemIndex(register(&item_names, d.to)),
+ duration: d.duration,
+ })
+ }
- let mut chef_spawn = Vec2::new(0., 0.);
- let mut customer_spawn = Vec2::new(0., 0.);
- let mut initial_map = HashMap::new();
- for (y, line) in map_in.map.iter().enumerate() {
- for (x, tile) in line.trim().chars().enumerate() {
- let pos = IVec2::new(x as i32, y as i32);
- if tile == map_in.chef_spawn {
- chef_spawn = pos.as_vec2() + Vec2::splat(0.5);
+ let mut chef_spawn = Vec2::new(0., 0.);
+ let mut customer_spawn = Vec2::new(0., 0.);
+ let mut initial_map = HashMap::new();
+ for (y, line) in map_in.map.iter().enumerate() {
+ for (x, tile) in line.trim().chars().enumerate() {
+ let pos = IVec2::new(x as i32, y as i32);
+ if tile == map_in.chef_spawn {
+ chef_spawn = pos.as_vec2() + Vec2::splat(0.5);
+ }
+ if tile == map_in.customer_spawn {
+ customer_spawn = pos.as_vec2() + Vec2::splat(0.5);
+ }
+ let tilename = map_in
+ .tiles
+ .get(&tile)
+ .ok_or(anyhow!("tile {tile} is undefined"))?
+ .clone();
+ let itemname = map_in.items.get(&tile).cloned();
+ let tile = TileIndex(register(&tile_names, tilename));
+ let item = itemname.map(|i| ItemIndex(register(&item_names, i)));
+ initial_map.insert(pos, (tile, item));
}
- if tile == map_in.customer_spawn {
- customer_spawn = pos.as_vec2() + Vec2::splat(0.5);
- }
- let tilename = map_in.tiles[&tile].clone();
- let itemname = map_in.items.get(&tile).cloned();
- let tile = TileIndex(register(&tile_names, tilename));
- let item = itemname.map(|i| ItemIndex(register(&item_names, i)));
- initial_map.insert(pos, (tile, item));
}
- }
- let item_names = item_names.into_inner().unwrap();
- let tile_names = tile_names.into_inner().unwrap();
+ let item_names = item_names.into_inner().unwrap();
+ let tile_names = tile_names.into_inner().unwrap();
- let tile_collide = tile_names
- .iter()
- .map(|i| !map_in.walkable.contains(i))
- .collect();
- let tile_interact = tile_names
- .iter()
- .map(|i| !map_in.collider.contains(i) && !map_in.walkable.contains(i))
- .collect();
+ let tile_collide = tile_names
+ .iter()
+ .map(|i| !map_in.walkable.contains(i))
+ .collect();
+ let tile_interact = tile_names
+ .iter()
+ .map(|i| !map_in.collider.contains(i) && !map_in.walkable.contains(i))
+ .collect();
- Gamedata {
- demands,
- tile_collide,
- tile_interact,
- recipes,
- initial_map,
- item_names,
- tile_names,
- chef_spawn,
- customer_spawn,
+ Ok(Gamedata {
+ demands,
+ tile_collide,
+ tile_interact,
+ recipes,
+ initial_map,
+ item_names,
+ tile_names,
+ chef_spawn,
+ customer_spawn,
+ })
}
}