diff options
| author | metamuffin <metamuffin@disroot.org> | 2024-06-19 23:21:45 +0200 | 
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2024-06-23 19:21:49 +0200 | 
| commit | 7be7848b65bd16139569389961541fcc52c905d2 (patch) | |
| tree | b2b5a26f9e98d82bc4b54fdf7f76d9dfd583aa0f /server/src/customer/mod.rs | |
| parent | 6ca76cc0568f3d60b280f11ae07a34303c317f34 (diff) | |
| download | hurrycurry-7be7848b65bd16139569389961541fcc52c905d2.tar hurrycurry-7be7848b65bd16139569389961541fcc52c905d2.tar.bz2 hurrycurry-7be7848b65bd16139569389961541fcc52c905d2.tar.zst | |
split up customer code. customer can enter, order and leave but not take meal
Diffstat (limited to 'server/src/customer/mod.rs')
| -rw-r--r-- | server/src/customer/mod.rs | 201 | 
1 files changed, 201 insertions, 0 deletions
| diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs new file mode 100644 index 00000000..ab3accdd --- /dev/null +++ b/server/src/customer/mod.rs @@ -0,0 +1,201 @@ +pub mod movement; +mod pathfinding; + +use crate::{ +    data::Gamedata, +    game::Game, +    protocol::{ItemIndex, Message, PacketC, PacketS, PlayerID}, +}; +use glam::{IVec2, Vec2}; +use log::{debug, error}; +use movement::MovementBase; +use pathfinding::{find_path, Path}; +use rand::thread_rng; +use std::{ +    collections::{HashMap, HashSet}, +    sync::Arc, +    time::Duration, +}; +use tokio::{ +    sync::{broadcast, RwLock}, +    time::interval, +}; + +struct DemandState { +    data: Gamedata, +    walkable: HashSet<IVec2>, +    chairs: HashMap<IVec2, bool>, +    items: HashMap<IVec2, ItemIndex>, +    customers: HashMap<PlayerID, Customer>, +} + +enum CustomerState { +    WalkingToChair { path: Path, chair: IVec2 }, +    Waiting { chair: IVec2, demand: ItemIndex }, +    Exiting { path: Path }, +} + +struct Customer { +    movement: MovementBase, +    state: CustomerState, +} + +pub async fn customer(game: Arc<RwLock<Game>>, mut grx: broadcast::Receiver<PacketC>) { +    let mut state = DemandState { +        walkable: Default::default(), +        chairs: Default::default(), +        items: Default::default(), +        customers: Default::default(), +        data: Gamedata::default(), +    }; +    let initial = game.write().await.prime_client(-1); +    for p in initial { +        match p { +            PacketC::Init { data, .. } => { +                state.data = data; +            } +            PacketC::UpdateMap { pos, tile, .. } => { +                let tilename = &state.data.tile_names[tile]; +                if tilename == "floor" || tilename == "door" || tilename == "chair" { +                    state.walkable.insert(pos); +                } +                if tilename == "chair" { +                    state.chairs.insert(pos, true); +                } +            } +            _ => (), +        } +    } + +    let mut interval = interval(Duration::from_millis(40)); +    let mut packets_out = Vec::new(); +    loop { +        tokio::select! { +            packet = grx.recv() => { +                match packet.unwrap() { +                    PacketC::PutItem { .. } +                    | PacketC::TakeItem { .. } +                    | PacketC::ProduceItem { .. } +                    | PacketC::ConsumeItem { .. } => { +                        let g = game.read().await; +                        update_items(&mut state, &g) +                    }, +                    _ => () +                } +            } +            _ = interval.tick() => { +                state.tick(&mut packets_out, 0.04); +                for (player,packet) in packets_out.drain(..) { +                    if let Err(e) = game.write().await.packet_in(player, packet) { +                        error!("customer misbehaved: {e}") +                    } +                } +            } +        } +    } +} + +fn update_items(state: &mut DemandState, 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 tick(&mut self, packets_out: &mut Vec<(PlayerID, PacketS)>, dt: f32) { +        if self.customers.is_empty() { +            let id = -1; +            packets_out.push(( +                id, +                PacketS::Join { +                    name: "George".to_string(), +                    character: 0, +                }, +            )); +            let chair = select_chair(&mut self.chairs); +            let path = find_path(&self.walkable, self.data.customer_spawn.as_ivec2(), chair) +                .expect("no path"); +            self.customers.insert( +                id, +                Customer { +                    movement: MovementBase { +                        position: self.data.customer_spawn, +                        facing: Vec2::X, +                        vel: Vec2::ZERO, +                    }, +                    state: CustomerState::WalkingToChair { path, chair }, +                }, +            ); +        } +        let mut customers_to_remove = Vec::new(); +        for (&id, p) in &mut self.customers { +            match &mut p.state { +                CustomerState::WalkingToChair { path, chair } => { +                    packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); +                    if path.is_done() { +                        let demand = self.data.get_item("tomato").unwrap(); +                        packets_out.push(( +                            id, +                            PacketS::Communicate { +                                message: Some(Message::Item(demand)), +                            }, +                        )); +                        p.state = CustomerState::Waiting { +                            chair: *chair, +                            demand, +                        }; +                    } +                } +                CustomerState::Waiting { chair, 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) { +                                Some(pos) +                            } else { +                                None +                            } +                        }); +                    if let Some(pos) = demand_pos { +                        if self.items.get(&pos) == Some(demand) { +                            packets_out.push((id, PacketS::Communicate { message: None })); +                            let path = find_path( +                                &self.walkable, +                                p.movement.position.as_ivec2(), +                                self.data.customer_spawn.as_ivec2(), +                            ) +                            .expect("no path to exit"); +                            p.state = CustomerState::Exiting { path } +                        } +                    } +                    debug!("waiting") +                } +                CustomerState::Exiting { path } => { +                    packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); +                    if path.is_done() { +                        packets_out.push((id, PacketS::Leave)); +                        customers_to_remove.push(id); +                    } +                } +            } +        } +        for c in customers_to_remove { +            self.customers.remove(&c).unwrap(); +        } +    } +} + +pub fn select_chair(chairs: &mut HashMap<IVec2, bool>) -> IVec2 { +    use rand::seq::IteratorRandom; +    let (chosen, free) = chairs +        .iter_mut() +        .filter(|(_p, free)| **free) +        .choose(&mut thread_rng()) +        .unwrap(); +    *free = false; +    *chosen +} | 
