diff options
| author | metamuffin <metamuffin@disroot.org> | 2024-06-17 17:39:39 +0200 | 
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2024-06-23 19:20:50 +0200 | 
| commit | 6f0424b9b4cddc0495eb673d314c570e27e61e83 (patch) | |
| tree | 3ca2f5c8f1d16020dfa432d8a93fb1f53be93c4b /server/src | |
| parent | 428fa6fb8dac18c541c0c231f1b640ba172e52b9 (diff) | |
| download | hurrycurry-6f0424b9b4cddc0495eb673d314c570e27e61e83.tar hurrycurry-6f0424b9b4cddc0495eb673d314c570e27e61e83.tar.bz2 hurrycurry-6f0424b9b4cddc0495eb673d314c570e27e61e83.tar.zst | |
everything indexed
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/game.rs | 133 | ||||
| -rw-r--r-- | server/src/interaction.rs | 66 | ||||
| -rw-r--r-- | server/src/lib.rs | 2 | ||||
| -rw-r--r-- | server/src/main.rs | 8 | ||||
| -rw-r--r-- | server/src/protocol.rs | 38 | ||||
| -rw-r--r-- | server/src/recipes.rs | 73 | 
6 files changed, 232 insertions, 88 deletions
| diff --git a/server/src/game.rs b/server/src/game.rs index a9ee8f3d..a7a07881 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,60 +1,81 @@ -use crate::protocol::{Item, PacketC, PacketS, Tile, ID}; +use crate::{ +    protocol::{ItemID, ItemIndex, PacketC, PacketS, PlayerID, TileIndex}, +    recipes::Gamedata, +};  use anyhow::{anyhow, Result};  use glam::IVec2; -use log::info; -use std::collections::{HashMap, VecDeque}; +use std::{ +    collections::{HashMap, VecDeque}, +    ops::Deref, +    sync::Arc, +}; -struct TileData { -    kind: Tile, -    items: Vec<ID>, +pub struct Tile { +    kind: TileIndex, +    items: Vec<ItemID>,      active: bool,      progress: f32,  }  struct Player {      name: String, -    hand: Option<ID>, +    hand: Option<ItemID>,  } -#[derive(Default)]  pub struct Game { -    item_id_counter: ID, -    tiles: HashMap<IVec2, TileData>, -    items: HashMap<ID, Item>, -    players: HashMap<ID, Player>, +    data: Arc<Gamedata>, +    item_id_counter: ItemID, +    tiles: HashMap<IVec2, Tile>, +    items: HashMap<ItemID, ItemIndex>, +    players: HashMap<PlayerID, Player>,      packet_out: VecDeque<PacketC>,  }  impl Game { -    pub fn new() -> Self { -        let mut g = Self::default(); +    pub fn new(gamedata: Arc<Gamedata>) -> Self { +        let mut g = Self { +            data: gamedata.clone(), +            item_id_counter: 0, +            items: Default::default(), +            packet_out: Default::default(), +            players: Default::default(), +            tiles: Default::default(), +        };          for x in -5..5 {              for y in -5..5 {                  g.tiles -                    .insert(IVec2 { x, y }, Tile("floor".to_string()).into()); +                    .insert(IVec2 { x, y }, gamedata.get_tile("floor").unwrap().into());              }          }          for x in -5..5 { -            g.tiles -                .insert(IVec2 { x, y: -5 }, Tile("table".to_string()).into()); -            g.tiles -                .insert(IVec2 { x, y: 4 }, Tile("table".to_string()).into()); +            g.tiles.insert( +                IVec2 { x, y: -5 }, +                gamedata.get_tile("table").unwrap().into(), +            ); +            g.tiles.insert( +                IVec2 { x, y: 4 }, +                gamedata.get_tile("table").unwrap().into(), +            );          }          for y in -5..5 { -            g.tiles -                .insert(IVec2 { x: -5, y }, Tile("table".to_string()).into()); -            g.tiles -                .insert(IVec2 { x: 4, y }, Tile("table".to_string()).into()); +            g.tiles.insert( +                IVec2 { x: -5, y }, +                gamedata.get_tile("table").unwrap().into(), +            ); +            g.tiles.insert( +                IVec2 { x: 4, y }, +                gamedata.get_tile("table").unwrap().into(), +            );          }          g.tiles.extend( -            [([-5, 1], "pan"), ([-5, 2], "pan"), ([4, 3], "flour_bag")].map(|(k, v)| { +            [([-5, 1], "pan"), ([-5, 2], "pan"), ([4, 3], "meat-spawn")].map(|(k, v)| {                  (                      IVec2::from_array(k), -                    TileData { +                    Tile {                          active: false,                          items: vec![], -                        kind: Tile(v.to_string()).into(), +                        kind: gamedata.get_tile(v).unwrap().into(),                          progress: 0.,                      },                  ) @@ -68,7 +89,7 @@ impl Game {          self.packet_out.pop_front()      } -    pub fn prime_client(&self, id: ID) -> Vec<PacketC> { +    pub fn prime_client(&self, id: PlayerID) -> Vec<PacketC> {          let mut out = Vec::new();          for (&id, player) in &self.players {              out.push(PacketC::AddPlayer { @@ -90,11 +111,14 @@ impl Game {                  })              }          } -        out.push(PacketC::Joined { id }); +        out.push(PacketC::Joined { +            id, +            data: self.data.deref().to_owned(), +        });          out      } -    pub fn packet_in(&mut self, player: ID, packet: PacketS) -> Result<()> { +    pub fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result<()> {          match packet {              PacketS::Join { name } => {                  self.players.insert( @@ -126,50 +150,27 @@ impl Game {                      .push_back(PacketC::Position { player, pos, rot });              }              PacketS::Interact { pos, edge } => { -                if !edge { -                    return Ok(()); -                } -                let tile = self -                    .tiles -                    .get_mut(&pos) -                    .ok_or(anyhow!("interacting with empty tile"))?; -                let player_data = self -                    .players -                    .get_mut(&player) -                    .ok_or(anyhow!("player does not exist"))?; -                if tile.kind.0 == "flour_bag" { -                    info!("new flour"); -                    let item = Item("flour".to_string()); -                    self.items.insert(self.item_id_counter, item.clone()); -                    tile.items.push(self.item_id_counter); -                    self.packet_out.push_back(PacketC::ProduceItem { -                        id: self.item_id_counter, -                        pos, -                        kind: item, -                    }); -                    self.item_id_counter += 1; -                } -                if let Some(item) = player_data.hand.take() { -                    info!("put {item}"); -                    tile.items.push(item); -                    self.packet_out.push_back(PacketC::PutItem { item, pos }) -                } else { -                    if let Some(item) = tile.items.pop() { -                        info!("take {item}"); -                        player_data.hand = Some(item); -                        self.packet_out -                            .push_back(PacketC::TakeItem { item, player }) -                    } -                } +                // if let Some(item) = player_data.hand.take() { +                //     info!("put {item}"); +                //     tile.items.push(item); +                //     self.packet_out.push_back(PacketC::PutItem { item, pos }) +                // } else { +                //     if let Some(item) = tile.items.pop() { +                //         info!("take {item}"); +                //         player_data.hand = Some(item); +                //         self.packet_out +                //             .push_back(PacketC::TakeItem { item, player }) +                //     } +                // }              }          }          Ok(())      }  } -impl From<Tile> for TileData { -    fn from(kind: Tile) -> Self { +impl From<TileIndex> for Tile { +    fn from(kind: TileIndex) -> Self {          Self {              kind,              progress: 0., diff --git a/server/src/interaction.rs b/server/src/interaction.rs new file mode 100644 index 00000000..5f8b0097 --- /dev/null +++ b/server/src/interaction.rs @@ -0,0 +1,66 @@ +use crate::{ +    protocol::{ItemIndex, TileIndex}, +    recipes::{Action, Gamedata}, +}; +use std::collections::BTreeSet; + +pub enum Out { +    Take(usize), +    Put, +    Produce(ItemIndex), +    Consume(usize), +} +use Out::*; + +pub fn interact( +    data: &Gamedata, +    edge: bool, +    tile: TileIndex, +    items: &[ItemIndex], +    hand: &Option<ItemIndex>, +    mut out: impl FnMut(Out), +) { +    let mut allowed = BTreeSet::new(); +    for r in &data.recipes { +        if r.tile == tile { +            allowed.extend(r.inputs.clone()) +        } +    } +    if !edge { +        return; +    } + +    let mut put_item = None; +    if let Some(hand) = hand { +        if allowed.contains(hand) { +            out(Put); +            put_item = Some(*hand); +        } +    } + +    for r in &data.recipes { +        let ok = r +            .inputs +            .iter() +            .all(|e| items.contains(e) || put_item == Some(*e)) +            && r.inputs.len() == items.len(); +        if ok { +            match r.action { +                Action::Passive(_) => todo!(), +                Action::Active(_) => todo!(), +                Action::Instant => { +                    for i in 0..items.len() { +                        out(Consume(i)) +                    } +                    for i in &r.outputs { +                        out(Produce(*i)); +                    } +                    if !r.outputs.is_empty() { +                        out(Take(r.outputs.len() - 1)); +                    } +                } +                Action::Never => (), +            } +        } +    } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 4659e440..5bb09e41 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,2 +1,4 @@  pub mod game;  pub mod protocol; +pub mod recipes; +pub mod interaction; diff --git a/server/src/main.rs b/server/src/main.rs index 7426e27e..441487e8 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,7 +1,7 @@  use anyhow::Result;  use futures_util::{SinkExt, StreamExt};  use log::{debug, info, warn}; -use std::{sync::Arc, time::Duration}; +use std::{fs::File, sync::Arc, time::Duration};  use tokio::{      io::{AsyncBufReadExt, AsyncWriteExt, BufReader},      net::TcpListener, @@ -13,6 +13,7 @@ use tokio_tungstenite::tungstenite::Message;  use undercooked::{      game::Game,      protocol::{PacketC, PacketS}, +    recipes::build_gamedata,  };  #[tokio::main] @@ -26,7 +27,10 @@ async fn main() -> Result<()> {      );      info!("listening for websockets on {}", ws_listener.local_addr()?); -    let game = Arc::new(RwLock::new(Game::new())); +    let data = +        build_gamedata(serde_yaml::from_reader(File::open("data/recipes.yaml").unwrap()).unwrap()); + +    let game = Arc::new(RwLock::new(Game::new(data.into())));      let (tx, rx) = broadcast::channel::<PacketC>(1024);      { diff --git a/server/src/protocol.rs b/server/src/protocol.rs index a318a9b2..ae87ffb4 100644 --- a/server/src/protocol.rs +++ b/server/src/protocol.rs @@ -1,15 +1,12 @@  use glam::{IVec2, Vec2};  use serde::{Deserialize, Serialize}; -pub type ID = u32; +use crate::recipes::Gamedata; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(transparent)] -pub struct Item(pub String); - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(transparent)] -pub struct Tile(pub String); +pub type PlayerID = usize; +pub type ItemID = usize; +pub type ItemIndex = usize; +pub type TileIndex = usize;  #[derive(Debug, Clone, Serialize, Deserialize)]  #[serde(rename_all = "snake_case")] @@ -24,36 +21,37 @@ pub enum PacketS {  #[serde(rename_all = "snake_case")]  pub enum PacketC {      Joined { -        id: ID, +        data: Gamedata, +        id: PlayerID,      },      AddPlayer { -        id: ID, +        id: PlayerID,          name: String, -        hand: Option<(ID, Item)>, +        hand: Option<(ItemID, ItemIndex)>,      },      RemovePlayer { -        id: ID, +        id: PlayerID,      },      Position { -        player: ID, +        player: PlayerID,          pos: Vec2,          rot: f32,      },      TakeItem { -        item: ID, -        player: ID, +        item: ItemID, +        player: PlayerID,      },      PutItem { -        item: ID, +        item: ItemID,          pos: IVec2,      },      ProduceItem { -        id: ID, +        id: ItemID,          pos: IVec2, -        kind: Item, +        kind: ItemIndex,      },      ConsumeItem { -        id: ID, +        id: ItemID,          pos: IVec2,      },      SetActive { @@ -62,6 +60,6 @@ pub enum PacketC {      },      UpdateMap {          pos: IVec2, -        tile: Tile, +        tile: TileIndex,      },  } diff --git a/server/src/recipes.rs b/server/src/recipes.rs new file mode 100644 index 00000000..25a4be98 --- /dev/null +++ b/server/src/recipes.rs @@ -0,0 +1,73 @@ +use crate::protocol::{ItemIndex, TileIndex}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, Clone, Copy)] +#[serde(rename_all = "snake_case")] +pub enum Action { +    Passive(f32), +    Active(f32), +    Instant, +    Never, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Recipe<T = TileIndex, I = ItemIndex> { +    pub tile: T, +    #[serde(default)] +    pub inputs: Vec<I>, +    #[serde(default)] +    pub outputs: Vec<I>, +    pub action: Action, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Gamedata { +    pub recipes: Vec<Recipe>, +    pub item_names: Vec<String>, +    pub tile_names: Vec<String>, +} +pub fn build_gamedata(recipes_in: Vec<Recipe<String, String>>) -> Gamedata { +    let mut item_names = Vec::new(); +    let mut tile_names = Vec::new(); +    let mut recipes = Vec::new(); + +    for r in recipes_in { +        recipes.push(Recipe { +            action: r.action, +            tile: register(&mut tile_names, r.tile.clone()), +            inputs: r +                .inputs +                .clone() +                .into_iter() +                .map(|e| register(&mut item_names, e)) +                .collect(), +            outputs: r +                .outputs +                .clone() +                .into_iter() +                .map(|e| register(&mut item_names, e)) +                .collect(), +        }) +    } + +    Gamedata { +        recipes, +        item_names, +        tile_names, +    } +} +fn register(db: &mut Vec<String>, name: String) -> usize { +    if let Some(index) = db.iter().position(|e| e == &name) { +        index +    } else { +        let index = db.len(); +        db.push(name); +        index +    } +} + +impl Gamedata { +    pub fn get_tile(&self, name: &str) -> Option<TileIndex> { +        self.tile_names.iter().position(|t| t == name) +    } +} | 
