summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/game.rs133
-rw-r--r--server/src/interaction.rs66
-rw-r--r--server/src/lib.rs2
-rw-r--r--server/src/main.rs8
-rw-r--r--server/src/protocol.rs38
-rw-r--r--server/src/recipes.rs73
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)
+ }
+}