diff options
Diffstat (limited to 'server')
| -rw-r--r-- | server/bot/src/algos/customer.rs | 14 | ||||
| -rw-r--r-- | server/bot/src/algos/simple.rs | 4 | ||||
| -rw-r--r-- | server/bot/src/main.rs | 3 | ||||
| -rw-r--r-- | server/client-lib/src/lib.rs | 62 | ||||
| -rw-r--r-- | server/protocol/Cargo.toml | 2 | ||||
| -rw-r--r-- | server/protocol/src/helpers.rs | 11 | ||||
| -rw-r--r-- | server/protocol/src/lib.rs | 9 | ||||
| -rw-r--r-- | server/src/data/mod.rs | 2 | ||||
| -rw-r--r-- | server/src/entity/bot.rs | 3 | ||||
| -rw-r--r-- | server/src/entity/tutorial.rs | 4 | ||||
| -rw-r--r-- | server/src/server.rs | 142 | 
11 files changed, 165 insertions, 91 deletions
| diff --git a/server/bot/src/algos/customer.rs b/server/bot/src/algos/customer.rs index b243bd55..b0ece9dd 100644 --- a/server/bot/src/algos/customer.rs +++ b/server/bot/src/algos/customer.rs @@ -22,7 +22,7 @@ use crate::{  use hurrycurry_client_lib::Game;  use hurrycurry_protocol::{      glam::{IVec2, Vec2}, -    DemandIndex, Message, PacketS, PlayerClass, PlayerID, Score, +    DemandIndex, Hand, Message, PacketS, PlayerClass, PlayerID, Score,  };  use log::info;  use rand::{random, seq::IndexedRandom, thread_rng}; @@ -313,6 +313,7 @@ impl CustomerState {                                  PacketS::Interact {                                      pos: Some(pos),                                      player: me, +                                    hand: Hand(0),                                  },                                  PacketS::ApplyScore(Score {                                      demands_completed: 1, @@ -322,6 +323,7 @@ impl CustomerState {                                  PacketS::Interact {                                      pos: None,                                      player: me, +                                    hand: Hand(0),                                  },                              ],                              ..Default::default() @@ -354,6 +356,7 @@ impl CustomerState {                          extra: vec![PacketS::ReplaceHand {                              player: me,                              item: demand.output, +                            hand: Hand(0),                          }],                          ..Default::default()                      }; @@ -369,7 +372,12 @@ impl CustomerState {                  cooldown,              } => {                  *cooldown -= dt; -                if game.players.get(&me).is_some_and(|pl| pl.item.is_none()) { +                if game +                    .players +                    .get(&me) +                    .is_some_and(|pl| pl.items[0].is_none()) +                // TODO index out of bounds? +                {                      if let Some(path) = find_path(&game.walkable, pos.as_ivec2(), *origin) {                          *self = CustomerState::Exiting { path };                      } @@ -383,10 +391,12 @@ impl CustomerState {                                  PacketS::Interact {                                      player: me,                                      pos: Some(*table), +                                    hand: Hand(0),                                  },                                  PacketS::Interact {                                      player: me,                                      pos: None, +                                    hand: Hand(0),                                  },                              ],                              direction, diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs index 14eb38c4..452f59d3 100644 --- a/server/bot/src/algos/simple.rs +++ b/server/bot/src/algos/simple.rs @@ -109,13 +109,13 @@ impl<S> Context<'_, S> {          self.game              .players              .get(&self.me) -            .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) +            .is_some_and(|p| p.items[0].as_ref().is_some_and(|i| i.kind == item))      }      pub fn is_hand_occupied(&self) -> bool {          self.game              .players              .get(&self.me) -            .map(|p| p.item.is_some()) +            .map(|p| p.items[0].is_some())              .unwrap_or(false)      }      pub fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs index 61ae1c1c..918be7e1 100644 --- a/server/bot/src/main.rs +++ b/server/bot/src/main.rs @@ -19,7 +19,7 @@ use anyhow::Result;  use clap::Parser;  use hurrycurry_bot::{algos::ALGO_CONSTRUCTORS, BotAlgo, BotInput};  use hurrycurry_client_lib::{network::sync::Network, Game}; -use hurrycurry_protocol::{PacketC, PacketS, PlayerClass, PlayerID}; +use hurrycurry_protocol::{Hand, PacketC, PacketS, PlayerClass, PlayerID};  use log::warn;  use std::{thread::sleep, time::Duration}; @@ -109,6 +109,7 @@ fn main() -> Result<()> {                  network.queue_out.push_back(PacketS::Interact {                      player: b.id,                      pos: interact, +                    hand: Hand(0),                  })              }              network.queue_out.push_back(PacketS::Movement { diff --git a/server/client-lib/src/lib.rs b/server/client-lib/src/lib.rs index 5d5e55d5..a40eafc1 100644 --- a/server/client-lib/src/lib.rs +++ b/server/client-lib/src/lib.rs @@ -20,7 +20,7 @@ pub mod network;  pub mod spatial_index;  use hurrycurry_protocol::{ -    glam::IVec2, movement::MovementBase, Gamedata, ItemIndex, ItemLocation, Message, +    glam::IVec2, movement::MovementBase, Gamedata, Hand, ItemIndex, ItemLocation, Message,      MessageTimeout, PacketC, PlayerClass, PlayerID, RecipeIndex, Score, TileIndex,  };  use spatial_index::SpatialIndex; @@ -54,8 +54,8 @@ pub struct Player {      pub name: String,      pub class: PlayerClass,      pub character: i32, -    pub interacting: Option<IVec2>, -    pub item: Option<Item>, +    pub interacting: Option<(IVec2, Hand)>, +    pub items: Vec<Option<Item>>,      pub communicate_persist: Option<(Message, MessageTimeout)>,      pub movement: MovementBase, @@ -95,7 +95,7 @@ impl Game {                          character,                          class,                          interacting: None, -                        item: None, +                        items: (0..self.data.hand_count).map(|_| None).collect(),                          communicate_persist: None,                          movement: MovementBase::new(position),                      }, @@ -117,15 +117,27 @@ impl Game {                      p.movement.rotation = rot;                  }              } -              PacketC::MoveItem { from, to } => { -                *self.get_item(to) = self.get_item(from).take(); +                if let Some(item) = self.get_item(to).map(|e| e.take()) { +                    if let Some(to) = self.get_item(from) { +                        *to = item; +                    } else { +                        // TODO perhaps restore to original position? +                    } +                }              }              PacketC::SetItem { location, item } => { -                *self.get_item(location) = item.map(|kind| Item { kind, active: None }); +                let location = self.get_item(location); +                if let Some(location) = location { +                    *location = item.map(|kind| Item { kind, active: None }); +                }              }              PacketC::ClearProgress { item } => { -                self.get_item(item).as_mut().unwrap().active = None; +                if let Some(slot) = self.get_item(item) { +                    if let Some(item) = slot { +                        item.active = None; +                    } +                }              }              PacketC::SetProgress {                  item, @@ -134,13 +146,17 @@ impl Game {                  speed,                  warn,              } => { -                self.get_item(item).as_mut().unwrap().active = Some(Involvement { -                    player, -                    speed, -                    warn, -                    position, -                    recipe: RecipeIndex(0), -                }); +                if let Some(slot) = self.get_item(item) { +                    if let Some(item) = slot { +                        item.active = Some(Involvement { +                            player, +                            speed, +                            warn, +                            position, +                            recipe: RecipeIndex(0), +                        }); +                    } +                }              }              PacketC::UpdateMap {                  tile, @@ -200,9 +216,11 @@ impl Game {          }          for player in self.players.values_mut() { -            if let Some(item) = &mut player.item { -                if let Some(active) = &mut item.active { -                    active.position += active.speed; +            for item in &mut player.items { +                if let Some(item) = item { +                    if let Some(active) = &mut item.active { +                        active.position += active.speed; +                    }                  }              }          } @@ -223,10 +241,12 @@ impl Game {          });      } -    pub fn get_item(&mut self, location: ItemLocation) -> &mut Option<Item> { +    pub fn get_item(&mut self, location: ItemLocation) -> Option<&mut Option<Item>> {          match location { -            ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, -            ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, +            ItemLocation::Tile(pos) => Some(&mut self.tiles.get_mut(&pos)?.item), +            ItemLocation::Player(pid, hand) => { +                Some(self.players.get_mut(&pid)?.items.get_mut(hand.0)?) +            }          }      }  } diff --git a/server/protocol/Cargo.toml b/server/protocol/Cargo.toml index 44533f9d..b6da120a 100644 --- a/server/protocol/Cargo.toml +++ b/server/protocol/Cargo.toml @@ -1,6 +1,6 @@  [package]  name = "hurrycurry-protocol" -version = "7.5.0" +version = "8.0.0"  edition = "2021"  [dependencies] diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs index 924d0886..b85c2f84 100644 --- a/server/protocol/src/helpers.rs +++ b/server/protocol/src/helpers.rs @@ -1,7 +1,8 @@  use std::fmt::Display;  use crate::{ -    DocumentElement, Gamedata, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, TileIndex, +    DocumentElement, Gamedata, Hand, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, +    TileIndex,  };  impl Gamedata { @@ -98,11 +99,17 @@ impl Display for ItemLocation {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {          match self {              ItemLocation::Tile(pos) => write!(f, "tile({pos})"), -            ItemLocation::Player(PlayerID(id)) => write!(f, "player({id})"), +            ItemLocation::Player(PlayerID(id), hand) => write!(f, "player({id}_{hand})"),          }      }  } +impl Display for Hand { +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +        write!(f, "h{}", self.0) +    } +} +  impl Default for DocumentElement {      fn default() -> Self {          Self::Document { es: vec![] } diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 2ef07015..74d463a1 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -71,6 +71,10 @@ pub struct RecipeIndex(pub usize);  #[serde(transparent)]  pub struct DemandIndex(pub usize); +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, Hash)] +#[serde(transparent)] +pub struct Hand(pub usize); +  #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]  pub struct MapMetadata {      pub name: String, @@ -98,6 +102,7 @@ pub struct Gamedata {      pub bot_algos: Vec<String>,      pub recipes: Vec<Recipe>,      pub demands: Vec<Demand>, +    pub hand_count: usize,  }  #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] @@ -124,6 +129,7 @@ pub enum PacketS {      },      Interact {          player: PlayerID, +        hand: Hand,          #[bincode(with_serde)]          pos: Option<IVec2>,      }, @@ -144,6 +150,7 @@ pub enum PacketS {      /// For internal use only (customers)      ReplaceHand {          player: PlayerID, +        hand: Hand,          item: Option<ItemIndex>,      },      #[serde(skip)] @@ -344,7 +351,7 @@ pub enum Recipe {  #[serde(rename_all = "snake_case")]  pub enum ItemLocation {      Tile(#[bincode(with_serde)] IVec2), -    Player(PlayerID), +    Player(PlayerID, Hand),  }  #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs index 57051fe9..64f40217 100644 --- a/server/src/data/mod.rs +++ b/server/src/data/mod.rs @@ -73,6 +73,7 @@ pub struct MapDecl {      walkable: Vec<String>,      chef_spawn: char,      customer_spawn: char, +    #[serde(default)] hand_count: Option<usize>,      #[serde(default)] entities: Vec<EntityDecl>,      #[serde(default)] tile_entities: HashMap<char, EntityDecl>,      #[serde(default)] score_baseline: i64, @@ -314,6 +315,7 @@ pub fn build_data(              item_names,              demands,              tile_names, +            hand_count: map_in.hand_count.unwrap_or(1),          },          Serverdata {              initial_map, diff --git a/server/src/entity/bot.rs b/server/src/entity/bot.rs index fe4d711f..6e6c9162 100644 --- a/server/src/entity/bot.rs +++ b/server/src/entity/bot.rs @@ -18,7 +18,7 @@  use super::{Entity, EntityContext};  use anyhow::Result;  use hurrycurry_bot::{BotAlgo, DynBotAlgo}; -use hurrycurry_protocol::{PacketS, PlayerClass, PlayerID}; +use hurrycurry_protocol::{Hand, PacketS, PlayerClass, PlayerID};  use log::info;  use rand::random;  use std::any::Any; @@ -72,6 +72,7 @@ impl<T: BotAlgo + Any> Entity for BotDriver<T> {              c.packet_in.push_back(PacketS::Interact {                  player: self.id,                  pos: input.interact, +                hand: Hand(0),              })          }          c.packet_in.push_back(PacketS::Movement { diff --git a/server/src/entity/tutorial.rs b/server/src/entity/tutorial.rs index 44244862..33c0e507 100644 --- a/server/src/entity/tutorial.rs +++ b/server/src/entity/tutorial.rs @@ -144,7 +144,7 @@ impl StepContext<'_> {              .game              .players              .get(&self.player) -            .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) +            .is_some_and(|p| p.items.iter().flatten().any(|i| i.kind == item))      }      pub fn find_demand(&self, item: ItemIndex) -> Option<IVec2> {          self.ent @@ -228,7 +228,7 @@ impl StepContext<'_> {              .game              .players              .get(&self.player) -            .is_some_and(|p| p.item.as_ref().is_some_and(|i| i.kind == item)) +            .is_some_and(|p| p.items.iter().flatten().any(|i| i.kind == item))          {              if let Some(pos) = self.find_demand(item) {                  Err((Some(pos), trm!("s.tutorial.serve"))) diff --git a/server/src/server.rs b/server/src/server.rs index 462e95d4..0889cd71 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -28,8 +28,8 @@ use hurrycurry_client_lib::{Game, Involvement, Item, Player, Tile};  use hurrycurry_protocol::{      glam::{IVec2, Vec2},      movement::MovementBase, -    Gamedata, ItemLocation, Menu, MessageTimeout, PacketC, PacketS, PlayerClass, PlayerID, Score, -    TileIndex, +    Gamedata, Hand, ItemLocation, Menu, MessageTimeout, PacketC, PacketS, PlayerClass, PlayerID, +    Score, TileIndex,  };  use log::{info, warn};  use rand::random; @@ -163,26 +163,28 @@ impl GameServerExt for Game {                  character: player.character,                  name: player.name.clone(),              }); -            if let Some(item) = &player.item { -                out.push(PacketC::SetItem { -                    location: ItemLocation::Player(id), -                    item: Some(item.kind), -                }); -                if let Some(Involvement { -                    player, -                    position, -                    speed, -                    warn, -                    .. -                }) = item.active -                { -                    out.push(PacketC::SetProgress { +            for (i, item) in player.items.iter().enumerate() { +                if let Some(item) = &item { +                    out.push(PacketC::SetItem { +                        location: ItemLocation::Player(id, Hand(i)), +                        item: Some(item.kind), +                    }); +                    if let Some(Involvement {                          player, -                        item: ItemLocation::Player(id),                          position,                          speed,                          warn, -                    }); +                        .. +                    }) = item.active +                    { +                        out.push(PacketC::SetProgress { +                            player, +                            item: ItemLocation::Player(id, Hand(i)), +                            position, +                            speed, +                            warn, +                        }); +                    }                  }              }              if let Some((message, timeout)) = &player.communicate_persist { @@ -252,7 +254,7 @@ impl GameServerExt for Game {          self.players.insert(              id,              Player { -                item: None, +                items: (0..self.data.hand_count).map(|_| None).collect(),                  character,                  class,                  movement: MovementBase::new(position), @@ -403,15 +405,18 @@ impl Server {                  self.game.players_spatial_index.remove_entry(player); -                if let Some(item) = p.item { -                    let pos = p.movement.position.floor().as_ivec2(); -                    if let Some(tile) = self.game.tiles.get_mut(&pos) { -                        if tile.item.is_none() { -                            self.packet_out.push_back(PacketC::SetItem { -                                location: ItemLocation::Tile(pos), -                                item: Some(item.kind), -                            }); -                            tile.item = Some(item); +                // TODO if holding two, one is destroyed +                for item in p.items { +                    if let Some(item) = item { +                        let pos = p.movement.position.floor().as_ivec2(); +                        if let Some(tile) = self.game.tiles.get_mut(&pos) { +                            if tile.item.is_none() { +                                self.packet_out.push_back(PacketC::SetItem { +                                    location: ItemLocation::Tile(pos), +                                    item: Some(item.kind), +                                }); +                                tile.item = Some(item); +                            }                          }                      }                  } @@ -449,7 +454,7 @@ impl Server {                      }                  }              } -            PacketS::Interact { pos, player } => { +            PacketS::Interact { pos, player, hand } => {                  for e in &mut self.entities {                      if e.interact(                          EntityContext { @@ -477,7 +482,9 @@ impl Server {                      .get_mut(&pid)                      .ok_or(tre!("s.error.no_player"))?; -                let (pos, edge) = match (pos, player.interacting) { +                let pos = pos.map(|p| (p, hand)); + +                let ((pos, hand), edge) = match (pos, player.interacting) {                      (None, None) => return Ok(()), // this is silent because of auto release                      (None, Some(pos)) => (pos, false),                      (Some(pos), None) => (pos, true), @@ -497,7 +504,7 @@ impl Server {                  // No going back from here on -                player.interacting = if edge { Some(pos) } else { None }; +                player.interacting = if edge { Some((pos, hand)) } else { None };                  let other_pid = if !self.game.data.is_tile_interactable(tile.kind) {                      self.game @@ -523,15 +530,18 @@ impl Server {                          return Err(tre!("s.error.customer_interact"));                      } +                    let this_hslot = this.items.get_mut(hand.0).ok_or(tre!("s.error.no_hand"))?; +                    let other_hslot = other.items.get_mut(hand.0).ok_or(tre!("s.error.no_hand"))?; +                      interact(                          &self.game.data,                          edge,                          None,                          Some(pid), -                        &mut this.item, -                        ItemLocation::Player(base_pid), -                        &mut other.item, -                        ItemLocation::Player(pid), +                        this_hslot, +                        ItemLocation::Player(base_pid, hand), +                        other_hslot, +                        ItemLocation::Player(pid, hand),                          &mut self.game.score,                          &mut self.score_changed,                          false, @@ -544,6 +554,11 @@ impl Server {                          .get_mut(&pid)                          .ok_or(tre!("s.error.no_player"))?; +                    let hslot = player +                        .items +                        .get_mut(hand.0) +                        .ok_or(tre!("s.error.no_hand"))?; +                      interact(                          &self.game.data,                          edge, @@ -551,8 +566,8 @@ impl Server {                          Some(pid),                          &mut tile.item,                          ItemLocation::Tile(pos), -                        &mut player.item, -                        ItemLocation::Player(pid), +                        hslot, +                        ItemLocation::Player(pid, hand),                          &mut self.game.score,                          &mut self.score_changed,                          false, @@ -592,14 +607,16 @@ impl Server {                      timeout,                  });              } -            PacketS::ReplaceHand { item, player } => { +            PacketS::ReplaceHand { item, player, hand } => {                  let pdata = self.game.players.get_mut(&player).ok_or(tre!(""))?; -                pdata.item = item.map(|i| Item { -                    kind: i, -                    active: None, -                }); +                if let Some(slot) = pdata.items.get_mut(hand.0) { +                    *slot = item.map(|i| Item { +                        kind: i, +                        active: None, +                    }); +                }                  self.packet_out.push_back(PacketC::SetItem { -                    location: ItemLocation::Player(player), +                    location: ItemLocation::Player(player, hand),                      item,                  })              } @@ -665,17 +682,19 @@ impl Server {                  rot: player.movement.rotation,              }); -            tick_slot( -                dt, -                &self.game.data, -                &self.gamedata_index, -                None, -                &mut player.item, -                ItemLocation::Player(pid), -                &mut self.game.score, -                &mut self.score_changed, -                &mut self.packet_out, -            ); +            for (i, item) in player.items.iter_mut().enumerate() { +                tick_slot( +                    dt, +                    &self.game.data, +                    &self.gamedata_index, +                    None, +                    item, +                    ItemLocation::Player(pid, Hand(i)), +                    &mut self.game.score, +                    &mut self.score_changed, +                    &mut self.packet_out, +                ); +            }          }          let mut players_auto_release = Vec::new(); @@ -686,20 +705,27 @@ impl Server {                      player.communicate_persist = None;                  }              } -            if let Some(pos) = player.interacting { +            if let Some((pos, hand)) = player.interacting {                  if let Some(tile) = self.game.tiles.get(&pos) {                      if let Some(item) = &tile.item {                          if let Some(involvement) = &item.active {                              if involvement.position >= 1. { -                                players_auto_release.push(*pid); +                                players_auto_release.push((*pid, hand));                              }                          }                      }                  }              }          } -        for player in players_auto_release.drain(..) { -            let _ = self.packet_in(PacketS::Interact { pos: None, player }, &mut vec![]); +        for (player, hand) in players_auto_release.drain(..) { +            let _ = self.packet_in( +                PacketS::Interact { +                    pos: None, +                    player, +                    hand, +                }, +                &mut vec![], +            );          }          let mut load_map = None; | 
