aboutsummaryrefslogtreecommitdiff
path: root/server/src/data/mod.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-07-18 15:42:11 +0200
committermetamuffin <metamuffin@disroot.org>2024-07-18 15:42:11 +0200
commit5f883c80e7fc63c97910d003c44aea814ab8a5d6 (patch)
treeb73a8c8f78db103671128e686136f08aa276923a /server/src/data/mod.rs
parentefc29c03f7be043ae8d037a93efce8cfa7c384cc (diff)
downloadhurrycurry-5f883c80e7fc63c97910d003c44aea814ab8a5d6.tar
hurrycurry-5f883c80e7fc63c97910d003c44aea814ab8a5d6.tar.bz2
hurrycurry-5f883c80e7fc63c97910d003c44aea814ab8a5d6.tar.zst
reimplement customers as entity
Diffstat (limited to 'server/src/data/mod.rs')
-rw-r--r--server/src/data/mod.rs388
1 files changed, 0 insertions, 388 deletions
diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs
deleted file mode 100644
index 28347a25..00000000
--- a/server/src/data/mod.rs
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- Hurry Curry! - a game about cooking
- Copyright 2024 metamuffin
- Copyright 2024 nokoe
-
- 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 crate::{
- entity::{construct_entity, Entity, EntityDecl},
- interaction::Recipe,
-};
-use anyhow::{anyhow, bail, Result};
-use demands::generate_demands;
-use hurrycurry_protocol::{
- glam::{IVec2, Vec2},
- DemandIndex, ItemIndex, MapMetadata, RecipeIndex, TileIndex,
-};
-use serde::{Deserialize, Serialize};
-use std::{
- collections::{HashMap, HashSet},
- fs::File,
- path::PathBuf,
- str::FromStr,
- sync::{Mutex, RwLock},
-};
-use tokio::fs::read_to_string;
-
-pub mod demands;
-
-#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default)]
-#[serde(rename_all = "snake_case")]
-pub enum Action {
- #[default]
- Never,
- Passive,
- Active,
- Instant,
- Demand,
-}
-
-#[derive(Debug, Clone, Deserialize, Serialize)]
-pub struct RecipeDecl {
- #[serde(default)]
- tile: Option<String>,
- #[serde(default)]
- inputs: Vec<String>,
- #[serde(default)]
- outputs: Vec<String>,
- #[serde(default)]
- action: Action,
- #[serde(default)]
- warn: bool,
- #[serde(default)]
- revert_duration: Option<f32>,
- #[serde(default)]
- duration: Option<f32>,
- #[serde(default)]
- points: Option<i64>,
-}
-
-#[derive(Debug, Clone, Deserialize)]
-pub struct InitialMap {
- map: Vec<String>,
- tiles: HashMap<char, String>,
- #[serde(default)]
- items: HashMap<char, String>,
- collider: Vec<String>,
- walkable: Vec<String>,
- chef_spawn: char,
- customer_spawn: char,
- #[serde(default)]
- entities: Vec<EntityDecl>,
- #[serde(default)]
- tile_entities: HashMap<char, EntityDecl>,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct DemandDecl {
- from: String,
- to: Option<String>,
- duration: f32,
- points: i64,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct Demand {
- pub from: ItemIndex,
- pub to: Option<ItemIndex>,
- pub duration: f32,
- pub points: i64,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-#[rustfmt::skip]
-pub struct Gamedata {
- pub spec: String,
- pub item_names: Vec<String>,
- pub tile_names: Vec<String>,
- pub tile_collide: Vec<bool>,
- pub tile_interact: Vec<bool>,
- pub map: HashMap<String, MapMetadata>,
- #[serde(skip)] pub recipes: Vec<Recipe>,
- #[serde(skip)] pub demands: Vec<Demand>,
- #[serde(skip)] pub initial_map: HashMap<IVec2, (TileIndex, Option<ItemIndex>)>,
- #[serde(skip)] pub chef_spawn: Vec2,
- #[serde(skip)] pub customer_spawn: Vec2,
- #[serde(skip)] pub entities: Vec<Entity>,
-}
-
-#[derive(Debug, Deserialize, Default)]
-pub struct DataIndex {
- pub maps: HashMap<String, MapMetadata>,
- pub demands: HashSet<String>,
- 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 reload(&mut self) -> Result<()> {
- *self = serde_yml::from_reader(File::open(data_dir().join("index.yaml"))?)?;
- Ok(())
- }
-
- pub async fn read_map(&self, name: &str) -> Result<String> {
- if !self.maps.contains_key(name) {
- bail!("unknown map: {name:?}");
- }
- let path = data_dir().join(format!("maps/{name}.yaml"));
- Ok(read_to_string(path).await?)
- }
- pub async fn read_demands(&self, name: &str) -> Result<String> {
- if !self.demands.contains(name) {
- bail!("unknown demands: {name:?}");
- }
- let path = data_dir().join(format!("demands/{name}.yaml"));
- Ok(read_to_string(path).await?)
- }
- pub async 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).await?)
- }
-
- pub async fn generate(&self, spec: String) -> Result<Gamedata> {
- let (map, recipes) = spec.split_once("-").unwrap_or((spec.as_str(), "default"));
-
- let map_in = serde_yml::from_str(&self.read_map(map).await?)?;
- let recipes_in = serde_yml::from_str(&self.read_recipes(recipes).await?)?;
-
- let mut gd = Gamedata::build(spec, map_in, recipes_in)?;
- gd.map = self.maps.clone();
- Ok(gd)
- }
-}
-
-impl Gamedata {
- pub fn build(spec: String, map_in: InitialMap, recipes_in: Vec<RecipeDecl>) -> Result<Self> {
- let reg = ItemTileRegistry::default();
- let mut recipes = Vec::new();
- let mut entities = Vec::new();
- let mut raw_demands = Vec::new();
-
- for mut r in recipes_in {
- let r2 = r.clone();
- let mut inputs = r.inputs.into_iter().map(|i| reg.register_item(i));
- let mut outputs = r.outputs.into_iter().map(|o| reg.register_item(o));
- let tile = r.tile.map(|t| reg.register_tile(t));
- match r.action {
- Action::Never => {}
- Action::Passive => recipes.push(Recipe::Passive {
- duration: r.duration.ok_or(anyhow!("duration for passive missing"))?,
- warn: r.warn,
- tile,
- revert_duration: r.revert_duration,
- input: inputs
- .next()
- .ok_or(anyhow!("passive recipe without input"))?,
- output: outputs.next(),
- }),
- Action::Active => recipes.push(Recipe::Active {
- duration: r.duration.ok_or(anyhow!("duration for active missing"))?,
- tile,
- input: inputs
- .next()
- .ok_or(anyhow!("active recipe without input"))?,
- outputs: [outputs.next(), outputs.next()],
- }),
- Action::Instant => {
- recipes.push(Recipe::Instant {
- points: r.points.take().unwrap_or(0),
- tile,
- inputs: [inputs.next(), inputs.next()],
- outputs: [outputs.next(), outputs.next()],
- });
- }
- Action::Demand => raw_demands.push((
- inputs.next().ok_or(anyhow!("demand needs inputs"))?,
- outputs.next(),
- r.duration.unwrap_or(10.),
- )),
- }
- assert_eq!(inputs.next(), None, "{r2:?} inputs left over");
- assert_eq!(outputs.next(), None, "{r2:?} outputs left over");
- assert_eq!(r.points, None, "points specified where not possible")
- }
-
- // TODO
- // for d in demands_in {
- // demands.push(Demand {
- // from: reg.register_item(d.from),
- // to: d.to.map(|to| reg.register_item(to)),
- // duration: d.duration,
- // points: d.points,
- // })
- // }
-
- let mut chef_spawn = Vec2::new(0., 0.);
- let mut customer_spawn = Vec2::new(0., 0.);
- let mut initial_map = HashMap::new();
- let mut tiles_used = HashSet::new();
- let mut items_used = HashSet::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();
- if let Some(ent) = map_in.tile_entities.get(&tile) {
- entities.push(construct_entity(Some(pos), ent, &reg)?);
- }
- let itemname = map_in.items.get(&tile).cloned();
- let tile = reg.register_tile(tilename);
- let item = itemname.map(|i| reg.register_item(i));
- tiles_used.insert(tile);
- if let Some(i) = item {
- items_used.insert(i);
- };
- initial_map.insert(pos, (tile, item));
- }
- }
-
- entities.extend(
- map_in
- .entities
- .iter()
- .map(|decl| construct_entity(None, decl, &reg))
- .try_collect::<Vec<_>>()?,
- );
-
- let demands = generate_demands(tiles_used, items_used, &raw_demands, &recipes);
-
- let item_names = reg.items.into_inner().unwrap();
- let tile_names = reg.tiles.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();
-
- Ok(Gamedata {
- spec,
- demands,
- tile_collide,
- tile_interact,
- recipes,
- map: HashMap::new(),
- initial_map,
- item_names,
- entities,
- tile_names,
- chef_spawn,
- customer_spawn,
- })
- }
-}
-
-#[derive(Default)]
-pub struct ItemTileRegistry {
- tiles: RwLock<Vec<String>>,
- items: RwLock<Vec<String>>,
-}
-
-impl ItemTileRegistry {
- pub fn register_tile(&self, name: String) -> TileIndex {
- TileIndex(Self::register(&self.tiles, name))
- }
- pub fn register_item(&self, name: String) -> ItemIndex {
- ItemIndex(Self::register(&self.items, name))
- }
- fn register(db: &RwLock<Vec<String>>, name: String) -> usize {
- let mut db = db.write().unwrap();
- if let Some(index) = db.iter().position(|e| e == &name) {
- index
- } else {
- let index = db.len();
- db.push(name);
- index
- }
- }
-}
-
-impl Gamedata {
- pub fn tile_name(&self, index: TileIndex) -> &String {
- &self.tile_names[index.0]
- }
- pub fn is_tile_colliding(&self, index: TileIndex) -> bool {
- self.tile_collide[index.0]
- }
- pub fn is_tile_interactable(&self, index: TileIndex) -> bool {
- self.tile_interact[index.0]
- }
- pub fn item_name(&self, index: ItemIndex) -> &String {
- &self.item_names[index.0]
- }
- pub fn recipe(&self, index: RecipeIndex) -> &Recipe {
- &self.recipes[index.0]
- }
- pub fn demand(&self, index: DemandIndex) -> &Demand {
- &self.demands[index.0]
- }
- pub fn get_tile_by_name(&self, name: &str) -> Option<TileIndex> {
- self.tile_names
- .iter()
- .position(|t| t == name)
- .map(TileIndex)
- }
- pub fn get_item_by_name(&self, name: &str) -> Option<ItemIndex> {
- self.item_names
- .iter()
- .position(|t| t == name)
- .map(ItemIndex)
- }
- pub fn recipes(&self) -> impl Iterator<Item = (RecipeIndex, &Recipe)> {
- self.recipes
- .iter()
- .enumerate()
- .map(|(i, e)| (RecipeIndex(i), e))
- }
-}
-/*
- Hurry Curry! - a game about cooking
- Copyright 2024 metamuffin
-
- 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/>.
-
-*/ \ No newline at end of file