diff options
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) + } +} |