Fix bug that prevented trades

This commit is contained in:
Jeff 2024-07-17 19:28:45 -04:00
parent 2a2a295cc2
commit 546346f707
2 changed files with 36 additions and 57 deletions

View File

@ -57,7 +57,7 @@ pub struct Bot {
sell_prices: PriceList, sell_prices: PriceList,
trade_mode: TradeMode, trade_mode: TradeMode,
previous_offer: Option<(HashMap<InvSlotId, u32>, HashMap<InvSlotId, u32>)>, previous_trade: Option<PendingTrade>,
last_trade_action: Instant, last_trade_action: Instant,
last_announcement: Instant, last_announcement: Instant,
last_ouch: Instant, last_ouch: Instant,
@ -153,7 +153,7 @@ impl Bot {
buy_prices, buy_prices,
sell_prices, sell_prices,
trade_mode: TradeMode::Trade, trade_mode: TradeMode::Trade,
previous_offer: None, previous_trade: None,
last_trade_action: now, last_trade_action: now,
last_announcement: now, last_announcement: now,
last_ouch: now, last_ouch: now,
@ -256,7 +256,7 @@ impl Bot {
if self.is_user_admin(&sender)? && !self.client.is_trading() { if self.is_user_admin(&sender)? && !self.client.is_trading() {
log::info!("Providing admin access"); log::info!("Providing admin access");
self.previous_offer = None; self.previous_trade = None;
self.trade_mode = TradeMode::AdminAccess; self.trade_mode = TradeMode::AdminAccess;
self.client.send_invite(sender, InviteKind::Trade); self.client.send_invite(sender, InviteKind::Trade);
@ -402,8 +402,8 @@ impl Bot {
match result { match result {
TradeResult::Completed => { TradeResult::Completed => {
if let Some(offer) = &self.previous_offer { if let Some(trade) = &self.previous_trade {
log::info!("Trade with {their_name}: {offer:?}",); log::info!("Trade with {their_name}: {:?}", trade.offers);
} }
self.client.send_command( self.client.send_command(
@ -531,8 +531,8 @@ impl Bot {
}; };
// If the trade hasn't changed, do nothing to avoid spamming the server. // If the trade hasn't changed, do nothing to avoid spamming the server.
if let Some(previous) = &self.previous_offer { if let Some(previous) = &self.previous_trade {
if (&previous.0, &previous.1) == (my_offer, their_offer) { if previous == &trade {
return Ok(()); return Ok(());
} }
} }
@ -627,7 +627,7 @@ impl Bot {
} }
if !self.sell_prices.0.contains_key(&item_id) { 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) { 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); drop(inventories);
let phase = trade.phase;
// Up until now there may have been an error, so we only update the previous offer now. // Up until now there may have been an error, so we only update the previous offer now.
// The trade action is infallible from here. // 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 // 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. // purchased. End this trade action if an item was removed.
if let Some((slot_id, quantity)) = my_item_to_remove { if let Some((slot_id, quantity)) = my_item_to_remove {
self.client.perform_trade_action(TradeAction::RemoveItem { self.client.perform_trade_action(TradeAction::RemoveItem {
item: *slot_id, item: slot_id,
quantity: *quantity, quantity,
ours: true, ours: true,
}); });
@ -667,8 +669,8 @@ impl Bot {
if let Some((slot_id, quantity)) = their_item_to_remove { if let Some((slot_id, quantity)) = their_item_to_remove {
self.client.perform_trade_action(TradeAction::RemoveItem { self.client.perform_trade_action(TradeAction::RemoveItem {
item: *slot_id, item: slot_id,
quantity: *quantity, quantity,
ours: false, ours: false,
}); });
@ -685,8 +687,7 @@ impl Bot {
// If the trade is balanced // If the trade is balanced
if difference == 0 { if difference == 0 {
// Accept // Accept
self.client self.client.perform_trade_action(TradeAction::Accept(phase));
.perform_trade_action(TradeAction::Accept(trade.phase));
// If they are offering more // If they are offering more
} else if difference > 0 { } else if difference > 0 {
// If they are offering coins // If they are offering coins
@ -748,7 +749,7 @@ impl Bot {
.to_string(); .to_string();
let mut found = false; 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()); let item_name = self.get_item_name(item_id.as_ref());
if item_name.to_lowercase().contains(&search_term) { 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()); let item_name = self.get_item_name(item_id.as_ref());
if item_name.to_lowercase().contains(&search_term) { if item_name.to_lowercase().contains(&search_term) {

View File

@ -1,16 +1,14 @@
/** /// Configuration used to initiate the bot.
Configuration used to initiate the bot. ///
/// The Config struct is used to store configuration values that are not sensitive. This includes
The Config struct is used to store configuration values that are not sensitive. This includes the /// the price data for items, the game server to connect to, and the position and orientation of
price data for items, the game server to connect to, and the position and orientation of the bot. /// the bot. The price lists have manual implementations for deserialization to allow turning
The price lists have manual implementations for deserialization to allow turning shortened item /// shortened item IDs into the full IDs used by the Veloren client.
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
The Secrets struct is used to store sensitive information that should not be shared. This should /// should be read from a separate file that is not checked into version control. In production,
be read from a separate file that is not checked into version control. In production, use a secure /// use a secure means of storing this information, such as the secret manager for Podman.
means of storing this information, such as the secret manager for Podman. use hashbrown::HashMap;
*/
use hashbrown::{hash_map, HashMap};
use serde::{ use serde::{
de::{self, Visitor}, de::{self, Visitor},
Deserialize, Deserialize,
@ -54,31 +52,13 @@ impl<'de> Deserialize<'de> for PriceList {
} }
} }
impl IntoIterator for PriceList {
type Item = (ItemDefinitionIdOwned, u32);
type IntoIter = hash_map::IntoIter<ItemDefinitionIdOwned, u32>;
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; pub struct PriceListVisitor;
impl<'de> Visitor<'de> for PriceListVisitor { impl<'de> Visitor<'de> for PriceListVisitor {
type Value = PriceList; type Value = PriceList;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 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<A>(self, mut map: A) -> Result<Self::Value, A::Error> fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
@ -95,7 +75,7 @@ impl<'de> Visitor<'de> for PriceListVisitor {
let mut secondary = secondary.to_string(); let mut secondary = secondary.to_string();
primary.insert_str(0, "common.items.modular.weapon.primary."); 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( let material = ItemDefinitionIdOwned::Simple(
material material
@ -108,15 +88,13 @@ impl<'de> Visitor<'de> for PriceListVisitor {
})? })?
.to_string(), .to_string(),
); );
let secondary = ItemDefinitionIdOwned::Compound {
// This unwrap is safe because the ItemDefinitionId is always Simple.
simple_base: primary,
components: vec![material],
};
ItemDefinitionIdOwned::Modular { ItemDefinitionIdOwned::Modular {
pseudo_base: "veloren.core.pseudo_items.modular.tool".to_string(), 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] => { [simple] => {