diff options
Diffstat (limited to 'server/src/customer/mod.rs')
-rw-r--r-- | server/src/customer/mod.rs | 205 |
1 files changed, 63 insertions, 142 deletions
diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs index 7f32b094..e6f999e6 100644 --- a/server/src/customer/mod.rs +++ b/server/src/customer/mod.rs @@ -20,39 +20,27 @@ mod pathfinding; use crate::{ data::Gamedata, - game::Game, - protocol::{DemandIndex, ItemIndex, Message, PacketC, PacketS, PlayerID}, - state::State, + game::Tile, + protocol::{DemandIndex, Message, PacketS, PlayerID}, }; use anyhow::{anyhow, Result}; use fake::{faker, Fake}; use glam::{IVec2, Vec2}; -use log::{debug, error, warn}; +use log::debug; use movement::MovementBase; use pathfinding::{find_path, Path}; use rand::{random, thread_rng}; use std::{ collections::{HashMap, HashSet}, sync::Arc, - time::Duration, -}; -use tokio::{ - sync::{broadcast, RwLock}, - time::interval, }; -struct CustomerManager { - disabled: bool, +pub struct DemandState { + data: Arc<Gamedata>, walkable: HashSet<IVec2>, chairs: HashMap<IVec2, bool>, - items: HashMap<IVec2, ItemIndex>, - customers: HashMap<PlayerID, Customer>, customer_id_counter: PlayerID, - demand: DemandState, -} - -struct DemandState { - data: Gamedata, + customers: HashMap<PlayerID, Customer>, } enum CustomerState { @@ -76,119 +64,41 @@ enum CustomerState { }, } -struct Customer { +pub struct Customer { movement: MovementBase, state: CustomerState, } -pub async fn customer(gstate: Arc<RwLock<State>>, mut grx: broadcast::Receiver<PacketC>) { - let mut state = CustomerManager { - disabled: true, - customer_id_counter: PlayerID(0), - walkable: Default::default(), - chairs: Default::default(), - items: Default::default(), - customers: Default::default(), - demand: DemandState { - data: Gamedata::default(), - }, - }; - let initial = gstate.write().await.game.prime_client(); - for packet in initial { - state.packet(packet); - } - - let mut interval = interval(Duration::from_millis(40)); - let mut packets_out = Vec::new(); - loop { - tokio::select! { - packet = grx.recv() => { - let packet = match packet { - Ok(p) => p, - Err(e) => { - warn!("{e}"); - continue; - } - }; - match packet { - PacketC::PutItem { .. } - | PacketC::TakeItem { .. } - | PacketC::SetTileItem { .. } => { - let g = gstate.read().await; - update_items(&mut state, &g.game) - }, - _ => () - } - state.packet(packet); - } - _ = interval.tick() => { - if !state.disabled { - if let Err(e) = state.tick(&mut packets_out, 0.04) { - warn!("error caught: {e}") - } - for (player,packet) in packets_out.drain(..) { - if let Err(e) = gstate.write().await.packet_in(player, packet).await { - error!("customer misbehaved: {e}") - } - } - } - } - } - } -} - -// TODO very inefficient, please do that incrementally -fn update_items(state: &mut CustomerManager, game: &Game) { - state.items.clear(); - for (&pos, tile) in game.tiles() { - if let Some(item) = &tile.item { - state.items.insert(pos, item.kind); +impl DemandState { + pub fn new(data: Arc<Gamedata>, map: &HashMap<IVec2, Tile>) -> Self { + let chair = data.get_tile_by_name("chair"); + Self { + walkable: map + .iter() + .filter(|(_, v)| !data.is_tile_colliding(v.kind)) + .map(|(e, _)| *e) + .collect(), + chairs: map + .iter() + .filter(|(_, v)| Some(v.kind) == chair) + .map(|(e, _)| (*e, true)) + .collect(), + customer_id_counter: PlayerID(0), + customers: Default::default(), + data, } } } impl DemandState { - pub fn target_customer_count(&self) -> usize { - // TODO insert sofa magic formula - 5 - } - pub fn generate_demand(&self) -> DemandIndex { - // TODO insert sofa magic formula - DemandIndex(random::<usize>() % self.data.demands.len()) - } -} - -impl CustomerManager { - pub fn packet(&mut self, packet: PacketC) { - match packet { - PacketC::Data { data } => { - self.disabled = data.demands.is_empty(); - self.demand.data = data; - } - PacketC::RemovePlayer { id } => { - self.customers.remove(&id); - } - PacketC::UpdateMap { - tile: pos, kind, .. - } => { - if let Some(kind) = kind { - let tilename = self.demand.data.tile_name(kind); - if !self.demand.data.is_tile_colliding(kind) { - self.walkable.insert(pos); - } - if tilename == "chair" { - self.chairs.insert(pos, true); - } - } else { - self.chairs.remove(&pos); - self.walkable.remove(&pos); - } - } - _ => (), - } - } - pub fn tick(&mut self, packets_out: &mut Vec<(PlayerID, PacketS)>, dt: f32) -> Result<()> { - if self.customers.len() < self.demand.target_customer_count() { + pub fn tick( + &mut self, + packets_out: &mut Vec<(PlayerID, PacketS)>, + tiles: &mut HashMap<IVec2, Tile>, + data: &Gamedata, + dt: f32, + ) -> Result<()> { + if self.customers.len() < 5 { self.customer_id_counter.0 -= 1; let id = self.customer_id_counter; packets_out.push(( @@ -198,14 +108,15 @@ impl CustomerManager { character: -2, }, )); - let chair = select_chair(&mut self.chairs).ok_or(anyhow!("no free chair found"))?; - let to = self.demand.data.customer_spawn.as_ivec2(); - let path = find_path(&self.walkable, to, chair).ok_or(anyhow!("no path to {to}"))?; + let chair = self.select_chair().ok_or(anyhow!("no free chair found"))?; + let from = data.customer_spawn.as_ivec2(); + let path = find_path(&self.walkable, from, chair) + .ok_or(anyhow!("no path from {from} to {chair}"))?; self.customers.insert( id, Customer { movement: MovementBase { - position: self.demand.data.customer_spawn, + position: data.customer_spawn, facing: Vec2::X, vel: Vec2::ZERO, }, @@ -220,11 +131,11 @@ impl CustomerManager { debug!("{id:?} entering"); packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); if path.is_done() { - let demand = self.demand.generate_demand(); + let demand = DemandIndex(random::<usize>() % self.data.demands.len()); packets_out.push(( id, PacketS::Communicate { - message: Some(Message::Item(self.demand.data.demand(demand).from)), + message: Some(Message::Item(data.demand(demand).from)), }, )); p.state = CustomerState::Waiting { @@ -246,18 +157,27 @@ impl CustomerManager { let path = find_path( &self.walkable, p.movement.position.as_ivec2(), - self.demand.data.customer_spawn.as_ivec2(), + data.customer_spawn.as_ivec2(), ) .expect("no path to exit"); *self.chairs.get_mut(&chair).unwrap() = true; p.state = CustomerState::Exiting { path } } else { - let demand_data = &self.demand.data.demand(*demand); + let demand_data = &data.demand(*demand); let demand_pos = [IVec2::NEG_X, IVec2::NEG_Y, IVec2::X, IVec2::Y] .into_iter() .find_map(|off| { let pos = *chair + off; - if self.items.get(&pos) == Some(&demand_data.from) { + if tiles + .get(&pos) + .map(|t| { + t.item + .as_ref() + .map(|i| i.kind == demand_data.from) + .unwrap_or_default() + }) + .unwrap_or_default() + { Some(pos) } else { None @@ -284,7 +204,7 @@ impl CustomerManager { chair, } => { debug!("{id:?} eating"); - let demand = self.demand.data.demand(*demand); + let demand = data.demand(*demand); *progress += dt / demand.duration; if *progress >= 1. { packets_out.push(( @@ -299,7 +219,7 @@ impl CustomerManager { let path = find_path( &self.walkable, p.movement.position.as_ivec2(), - self.demand.data.customer_spawn.as_ivec2(), + data.customer_spawn.as_ivec2(), ) .ok_or(anyhow!("no path to exit"))?; *self.chairs.get_mut(&chair).unwrap() = true; @@ -321,14 +241,15 @@ impl CustomerManager { } Ok(()) } -} -pub fn select_chair(chairs: &mut HashMap<IVec2, bool>) -> Option<IVec2> { - use rand::seq::IteratorRandom; - let (chosen, free) = chairs - .iter_mut() - .filter(|(_p, free)| **free) - .choose(&mut thread_rng())?; - *free = false; - Some(*chosen) + pub fn select_chair(&mut self) -> Option<IVec2> { + use rand::seq::IteratorRandom; + let (chosen, free) = self + .chairs + .iter_mut() + .filter(|(_p, free)| **free) + .choose(&mut thread_rng())?; + *free = false; + Some(*chosen) + } } |