From 0f94e292bde8b9614aa48a6ba87f1a8d927b8133 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 13 Aug 2024 14:50:10 +0200 Subject: replace customers with bots and refactor some more server code. --- server/src/data.rs | 354 ----------------------------------------------------- 1 file changed, 354 deletions(-) delete mode 100644 server/src/data.rs (limited to 'server/src/data.rs') diff --git a/server/src/data.rs b/server/src/data.rs deleted file mode 100644 index 021de525..00000000 --- a/server/src/data.rs +++ /dev/null @@ -1,354 +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 . - -*/ -use crate::entity::{construct_entity, Entities, EntityDecl}; -use anyhow::{anyhow, bail, Result}; -use hurrycurry_protocol::{ - glam::{IVec2, Vec2}, - Gamedata, ItemIndex, MapMetadata, Recipe, 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; - -#[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, - #[serde(default)] - inputs: Vec, - #[serde(default)] - outputs: Vec, - #[serde(default)] - action: Action, - #[serde(default)] - warn: bool, - #[serde(default)] - revert_duration: Option, - #[serde(default)] - duration: Option, - #[serde(default)] - points: Option, -} - -#[derive(Debug, Clone, Deserialize)] -pub struct InitialMap { - map: Vec, - tiles: HashMap, - #[serde(default)] - items: HashMap, - collider: Vec, - walkable: Vec, - chef_spawn: char, - customer_spawn: char, - #[serde(default)] - entities: Vec, - #[serde(default)] - tile_entities: HashMap, - #[serde(default)] - score_baseline: i64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DemandDecl { - from: String, - to: Option, - duration: f32, - points: i64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Demand { - pub from: ItemIndex, - pub to: Option, - pub duration: f32, - pub points: i64, -} - -#[derive(Debug,Clone, Default)] -#[rustfmt::skip] -pub struct Serverdata { - pub spec: String, - pub initial_map: HashMap)>, - pub chef_spawn: Vec2, - pub customer_spawn: Vec2, - pub score_baseline: i64, -} - -#[derive(Debug, Deserialize, Default)] -pub struct DataIndex { - pub maps: HashMap, - pub recipes: HashSet, -} - -pub static DATA_DIR: Mutex> = 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 { - 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_recipes(&self, name: &str) -> Result { - 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, Serverdata, Entities)> { - 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?)?; - - Ok(build_data( - self.maps.clone(), - spec.clone(), - map.to_string(), - map_in, - recipes_in, - )?) - } -} - -pub fn build_data( - maps: HashMap, - spec: String, - map_name: String, - map_in: InitialMap, - recipes_in: Vec, -) -> Result<(Gamedata, Serverdata, Entities)> { - 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.chars().enumerate() { - if tile == ' ' { - continue; // space is empty space - } - 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 = 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)); - } - } - - 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 let Some(ent) = map_in.tile_entities.get(&tile) { - entities.push(construct_entity( - Some(pos), - ent, - ®, - &tiles_used, - &items_used, - &raw_demands, - &recipes, - &initial_map, - )?); - } - } - } - - entities.extend( - map_in - .entities - .iter() - .map(|decl| { - construct_entity( - None, - decl, - ®, - &tiles_used, - &items_used, - &raw_demands, - &recipes, - &initial_map, - ) - }) - .try_collect::>()?, - ); - - 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 { - current_map: map_name, - maps, - tile_collide, - tile_interact, - recipes, - item_names, - tile_names, - }, - Serverdata { - spec, - initial_map, - chef_spawn, - customer_spawn, - score_baseline: map_in.score_baseline, - }, - entities, - )) -} - -#[derive(Default)] -pub struct ItemTileRegistry { - tiles: RwLock>, - items: RwLock>, -} - -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>, 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 - } - } -} -- cgit v1.2.3-70-g09d2