diff --git a/src/bot.rs b/src/bot.rs index 849bd7f..58ff360 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -57,7 +57,7 @@ pub struct Bot { sell_prices: PriceList, trade_mode: TradeMode, - previous_offer: Option<(HashMap, HashMap)>, + previous_trade: Option, last_trade_action: Instant, last_announcement: Instant, last_ouch: Instant, @@ -153,7 +153,7 @@ impl Bot { buy_prices, sell_prices, trade_mode: TradeMode::Trade, - previous_offer: None, + previous_trade: None, last_trade_action: now, last_announcement: now, last_ouch: now, @@ -256,7 +256,7 @@ impl Bot { if self.is_user_admin(&sender)? && !self.client.is_trading() { log::info!("Providing admin access"); - self.previous_offer = None; + self.previous_trade = None; self.trade_mode = TradeMode::AdminAccess; self.client.send_invite(sender, InviteKind::Trade); @@ -402,8 +402,8 @@ impl Bot { match result { TradeResult::Completed => { - if let Some(offer) = &self.previous_offer { - log::info!("Trade with {their_name}: {offer:?}",); + if let Some(trade) = &self.previous_trade { + log::info!("Trade with {their_name}: {:?}", trade.offers); } self.client.send_command( @@ -531,8 +531,8 @@ impl Bot { }; // If the trade hasn't changed, do nothing to avoid spamming the server. - if let Some(previous) = &self.previous_offer { - if (&previous.0, &previous.1) == (my_offer, their_offer) { + if let Some(previous) = &self.previous_trade { + if previous == &trade { return Ok(()); } } @@ -627,7 +627,7 @@ impl Bot { } if !self.sell_prices.0.contains_key(&item_id) { - my_item_to_remove = Some((slot_id, amount)); + my_item_to_remove = Some((*slot_id, *amount)); } } @@ -642,23 +642,25 @@ impl Bot { } if !self.buy_prices.0.contains_key(&item_id) { - their_item_to_remove = Some((slot_id, amount)); + their_item_to_remove = Some((*slot_id, *amount)); } } drop(inventories); + let phase = trade.phase; + // Up until now there may have been an error, so we only update the previous offer now. // The trade action is infallible from here. - self.previous_offer = Some((my_offer.clone(), their_offer.clone())); + self.previous_trade = Some(trade); // Before running any actual trade logic, remove items that are not for sale or not being // purchased. End this trade action if an item was removed. if let Some((slot_id, quantity)) = my_item_to_remove { self.client.perform_trade_action(TradeAction::RemoveItem { - item: *slot_id, - quantity: *quantity, + item: slot_id, + quantity, ours: true, }); @@ -667,8 +669,8 @@ impl Bot { if let Some((slot_id, quantity)) = their_item_to_remove { self.client.perform_trade_action(TradeAction::RemoveItem { - item: *slot_id, - quantity: *quantity, + item: slot_id, + quantity, ours: false, }); @@ -685,8 +687,7 @@ impl Bot { // If the trade is balanced if difference == 0 { // Accept - self.client - .perform_trade_action(TradeAction::Accept(trade.phase)); + self.client.perform_trade_action(TradeAction::Accept(phase)); // If they are offering more } else if difference > 0 { // If they are offering coins @@ -748,7 +749,7 @@ impl Bot { .to_string(); let mut found = false; - for (item_id, price) in &self.buy_prices { + for (item_id, price) in &self.buy_prices.0 { let item_name = self.get_item_name(item_id.as_ref()); if item_name.to_lowercase().contains(&search_term) { @@ -784,7 +785,7 @@ impl Bot { } } - for (item_id, price) in &self.sell_prices { + for (item_id, price) in &self.sell_prices.0 { let item_name = self.get_item_name(item_id.as_ref()); if item_name.to_lowercase().contains(&search_term) { diff --git a/src/config.rs b/src/config.rs index e263b34..84c916e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,14 @@ -/** -Configuration used to initiate the bot. - -The Config struct is used to store configuration values that are not sensitive. This includes the -price data for items, the game server to connect to, and the position and orientation of the bot. -The price lists have manual implementations for deserialization to allow turning shortened item -IDs into the full IDs used by the Veloren client. - -The Secrets struct is used to store sensitive information that should not be shared. This should -be read from a separate file that is not checked into version control. In production, use a secure -means of storing this information, such as the secret manager for Podman. -*/ -use hashbrown::{hash_map, HashMap}; +/// Configuration used to initiate the bot. +/// +/// The Config struct is used to store configuration values that are not sensitive. This includes +/// the price data for items, the game server to connect to, and the position and orientation of +/// the bot. The price lists have manual implementations for deserialization to allow turning +/// shortened item IDs into the full IDs used by the Veloren client. +/// +/// The Secrets struct is used to store sensitive information that should not be shared. This +/// should be read from a separate file that is not checked into version control. In production, +/// use a secure means of storing this information, such as the secret manager for Podman. +use hashbrown::HashMap; use serde::{ de::{self, Visitor}, Deserialize, @@ -54,31 +52,13 @@ impl<'de> Deserialize<'de> for PriceList { } } -impl IntoIterator for PriceList { - type Item = (ItemDefinitionIdOwned, u32); - type IntoIter = hash_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a PriceList { - type Item = (&'a ItemDefinitionIdOwned, &'a u32); - type IntoIter = hash_map::Iter<'a, ItemDefinitionIdOwned, u32>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - pub struct PriceListVisitor; impl<'de> Visitor<'de> for PriceListVisitor { type Value = PriceList; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a map with simple and/or modular keys: Material|primary|secondary") + formatter.write_str("a map with simple and/or modular keys: material|primary|secondary") } fn visit_map(self, mut map: A) -> Result @@ -95,7 +75,7 @@ impl<'de> Visitor<'de> for PriceListVisitor { let mut secondary = secondary.to_string(); primary.insert_str(0, "common.items.modular.weapon.primary."); - secondary.insert_str(0, "common.items.modular.weapon.secondar."); + secondary.insert_str(0, "common.items.modular.weapon.secondary."); let material = ItemDefinitionIdOwned::Simple( material @@ -108,15 +88,13 @@ impl<'de> Visitor<'de> for PriceListVisitor { })? .to_string(), ); - let secondary = ItemDefinitionIdOwned::Compound { - // This unwrap is safe because the ItemDefinitionId is always Simple. - simple_base: primary, - components: vec![material], - }; ItemDefinitionIdOwned::Modular { pseudo_base: "veloren.core.pseudo_items.modular.tool".to_string(), - components: vec![secondary], + components: vec![ItemDefinitionIdOwned::Compound { + simple_base: primary, + components: vec![ItemDefinitionIdOwned::Simple(secondary), material], + }], } } [simple] => {