Compare commits

...

4 Commits

Author SHA1 Message Date
91d0f50fd4 Clean up with clippy; Try gitea action
Some checks failed
Podman Image CI / build (push) Failing after 40s
2024-07-18 06:03:00 -04:00
385fe651d4 Notify player of unbalanced trade value 2024-07-18 02:53:17 -04:00
9f0db64482 Clean up 2024-07-17 23:56:07 -04:00
3e6aea5668 Define timers with consts 2024-07-17 23:52:45 -04:00
3 changed files with 129 additions and 49 deletions

View File

@ -0,0 +1,20 @@
name: Podman Image CI
on:
push:
branches: ["main"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Build the image
run: podman build --tag git.jeffa.io/jeff/trade_bot .
- name: Push the image
run: podman push git.jeffa.io/jeff/trade_bot

View File

@ -6,6 +6,7 @@ orientation = 0
"iron|sword.greatsword|sword.long" = 1_000 "iron|sword.greatsword|sword.long" = 1_000
[sell_prices] [sell_prices]
"food.cheese" = 1_000_000
"consumable.potion_minor" = 150 "consumable.potion_minor" = 150
# Modular Weapons # Modular Weapons

View File

@ -33,18 +33,13 @@ use crate::config::PriceList;
const COINS: ItemDefinitionId = const COINS: ItemDefinitionId =
ItemDefinitionId::Simple(Cow::Borrowed("common.items.utility.coins")); ItemDefinitionId::Simple(Cow::Borrowed("common.items.utility.coins"));
const CLIENT_TPS: Duration = Duration::from_millis(33);
/// TODO: Implement Display const TRADE_ACTION_DELAY: Duration = Duration::from_millis(300);
#[derive(Debug)] const ACCOUNCEMENT_DELAY: Duration = Duration::from_mins(45);
pub struct Reciept { const OUCH_DELAY: Duration = Duration::from_secs(2);
pub my_items: HashMap<String, u32>,
pub their_items: HashMap<String, u32>,
}
/// An active connection to the Veloren server that will attempt to run every time the `tick` /// An active connection to the Veloren server that will attempt to run every time the `tick`
/// function is called. /// function is called.
///
/// See the [module-level documentation](index.html) for more information.
pub struct Bot { pub struct Bot {
username: String, username: String,
position: Pos, position: Pos,
@ -91,7 +86,7 @@ impl Bot {
log::info!("Connecting to veloren"); log::info!("Connecting to veloren");
let mut client = connect_to_veloren(game_server, auth_server, &username, password)?; let mut client = connect_to_veloren(game_server, auth_server, &username, password)?;
let mut clock = Clock::new(Duration::from_secs_f64(1.0 / 30.0)); let mut clock = Clock::new(CLIENT_TPS);
client.load_character_list(); client.load_character_list();
@ -134,10 +129,7 @@ impl Bot {
let position = if let Some(coords) = position { let position = if let Some(coords) = position {
Pos(coords.into()) Pos(coords.into())
} else { } else {
client client.position().map(Pos).ok_or("Failed to get position")?
.position()
.map(|coords| Pos(coords))
.ok_or("Failed to get position")?
}; };
let orientation = if let Some(orientation) = orientation { let orientation = if let Some(orientation) = orientation {
Ori::new(Quaternion::rotation_z(orientation.to_radians())) Ori::new(Quaternion::rotation_z(orientation.to_radians()))
@ -192,7 +184,7 @@ impl Bot {
self.handle_veloren_event(event)?; self.handle_veloren_event(event)?;
} }
if self.last_trade_action.elapsed() > Duration::from_millis(300) { if self.last_trade_action.elapsed() > TRADE_ACTION_DELAY {
self.client.respawn(); self.client.respawn();
self.handle_position_and_orientation()?; self.handle_position_and_orientation()?;
self.handle_lantern(); self.handle_lantern();
@ -232,7 +224,7 @@ impl Bot {
} }
} }
if self.last_announcement.elapsed() > Duration::from_mins(45) { if self.last_announcement.elapsed() > ACCOUNCEMENT_DELAY {
self.handle_announcement()?; self.handle_announcement()?;
self.last_announcement = Instant::now(); self.last_announcement = Instant::now();
@ -305,7 +297,7 @@ impl Bot {
} }
"price" => { "price" => {
for item_name in split_content { for item_name in split_content {
self.send_price_info(&sender, &item_name)?; self.send_price_info(&sender, item_name)?;
} }
None None
@ -372,7 +364,7 @@ impl Bot {
.. ..
}) => { }) => {
if let Some(uid) = self.client.uid() { if let Some(uid) = self.client.uid() {
if uid == target && self.last_ouch.elapsed() > Duration::from_secs(2) { if uid == target && self.last_ouch.elapsed() > OUCH_DELAY {
self.client self.client
.send_command("say".to_string(), vec!["Ouch!".to_string()]); .send_command("say".to_string(), vec!["Ouch!".to_string()]);
@ -388,7 +380,7 @@ impl Bot {
if let Some(uid) = self.client.uid() { if let Some(uid) = self.client.uid() {
if uid == info.target if uid == info.target
&& info.amount.is_sign_negative() && info.amount.is_sign_negative()
&& self.last_ouch.elapsed() > Duration::from_secs(1) && self.last_ouch.elapsed() > OUCH_DELAY
{ {
self.client self.client
.send_command("say".to_string(), vec!["That hurt!".to_string()]); .send_command("say".to_string(), vec!["That hurt!".to_string()]);
@ -519,6 +511,8 @@ impl Bot {
/// 6. If the total value of my offer is greater than the total value of their offer: /// 6. If the total value of my offer is greater than the total value of their offer:
/// 1. If I am offering coins, remove them to balance. /// 1. If I am offering coins, remove them to balance.
/// 2. If I am not offering coins, add theirs to balance. /// 2. If I am not offering coins, add theirs to balance.
/// 7. If the trade is still unbalanced, tell them the value of the greater offer and the
/// other party's total coin amount.
/// ///
/// See the inline comments for more details. /// See the inline comments for more details.
#[allow(clippy::comparison_chain)] #[allow(clippy::comparison_chain)]
@ -527,6 +521,7 @@ impl Bot {
return Ok(()); return Ok(());
} }
let phase = trade.phase;
let my_offer_index = trade let my_offer_index = trade
.which_party(self.client.uid().ok_or("Failed to get uid")?) .which_party(self.client.uid().ok_or("Failed to get uid")?)
.ok_or("Failed to get offer index")?; .ok_or("Failed to get offer index")?;
@ -537,6 +532,11 @@ impl Bot {
&trade.offers[their_offer_index], &trade.offers[their_offer_index],
) )
}; };
let their_uid = trade.parties[their_offer_index];
let their_name = self
.find_player_alias(&their_uid)
.ok_or("Failed to find player name")?
.clone();
let inventories = self.client.inventories(); let inventories = self.client.inventories();
let me = self.client.entity(); let me = self.client.entity();
@ -554,6 +554,16 @@ impl Bot {
let coins_owned = COINS.to_owned(); let coins_owned = COINS.to_owned();
let get_my_coins = my_inventory.get_slot_of_item_by_def_id(&coins_owned); let get_my_coins = my_inventory.get_slot_of_item_by_def_id(&coins_owned);
let get_their_coins = their_inventory.get_slot_of_item_by_def_id(&coins_owned); let get_their_coins = their_inventory.get_slot_of_item_by_def_id(&coins_owned);
let my_coin_amount = if let Some(coins) = get_my_coins {
my_inventory.get(coins).unwrap().amount()
} else {
0
};
let their_coin_amount = if let Some(coins) = get_their_coins {
their_inventory.get(coins).unwrap().amount()
} else {
0
};
let mut receipt = Reciept { let mut receipt = Reciept {
my_items: HashMap::new(), my_items: HashMap::new(),
their_items: HashMap::new(), their_items: HashMap::new(),
@ -659,8 +669,6 @@ impl Bot {
drop(inventories); drop(inventories);
let phase = trade.phase;
// 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_trade { if let Some(previous) = &self.previous_trade {
if previous == &trade { if previous == &trade {
@ -672,6 +680,8 @@ impl Bot {
// offer now. The trade action is infallible from here. // offer now. The trade action is infallible from here.
self.previous_trade = Some(trade); self.previous_trade = Some(trade);
log::info!("Performing trade action with {their_name}");
// 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.
@ -699,8 +709,7 @@ impl Bot {
// The if/else statements below implement the bot's main feature: buying, selling and // The if/else statements below implement the bot's main feature: buying, selling and
// trading items according to the values set in the configuration file. Coins are used to // trading items according to the values set in the configuration file. Coins are used to
// balance the value of the trade. In the case that we try to add more coins than are // balance the value of the trade.
// available, the server will correct it by adding all of the available coins.
// If the trade is balanced // If the trade is balanced
if difference == 0 { if difference == 0 {
@ -708,50 +717,94 @@ impl Bot {
// Accept // Accept
self.client.perform_trade_action(TradeAction::Accept(phase)); self.client.perform_trade_action(TradeAction::Accept(phase));
return Ok(());
}
// If they are offering more // If they are offering more
} else if difference > 0 { if difference > 0 {
// If they are offering coins // If they are offering coins
if their_offered_coin_amount > 0 { if their_offered_coin_amount > 0 {
if let Some(their_coins) = get_their_coins { if let Some(their_coins) = get_their_coins {
if their_coin_amount >= difference as u32 {
// Remove their coins to balance // Remove their coins to balance
self.client.perform_trade_action(TradeAction::RemoveItem { self.client.perform_trade_action(TradeAction::RemoveItem {
item: their_coins, item: their_coins,
quantity: difference as u32, quantity: difference as u32,
ours: false, ours: false,
}); });
return Ok(());
}
} }
// If they are not offering coins // If they are not offering coins
} else if let Some(my_coins) = get_my_coins { } else if let Some(my_coins) = get_my_coins {
if my_coin_amount >= difference as u32 {
// Add my coins to balanace // Add my coins to balanace
self.client.perform_trade_action(TradeAction::AddItem { self.client.perform_trade_action(TradeAction::AddItem {
item: my_coins, item: my_coins,
quantity: difference as u32, quantity: difference as u32,
ours: true, ours: true,
}); });
return Ok(());
} }
}
self.client.send_command(
"tell".to_string(),
vec![
their_name,
format!(
"The value of your offer is {their_offered_items_value} coins. I only have {my_coin_amount} coins.",
),
],
);
}
// If I am offering more // If I am offering more
} else if difference < 0 { if difference < 0 {
// If I am offering coins // If I am offering coins
if my_offered_coin_amount > 0 { if my_offered_coin_amount > 0 {
if let Some(my_coins) = get_my_coins { if let Some(my_coins) = get_my_coins {
if my_coin_amount >= difference.unsigned_abs() {
// Remove my coins to balance // Remove my coins to balance
self.client.perform_trade_action(TradeAction::RemoveItem { self.client.perform_trade_action(TradeAction::RemoveItem {
item: my_coins, item: my_coins,
quantity: difference.unsigned_abs(), quantity: difference.unsigned_abs(),
ours: true, ours: true,
}); });
return Ok(());
}
} }
// If I am not offering coins // If I am not offering coins
} else if let Some(their_coins) = get_their_coins { } else if let Some(their_coins) = get_their_coins {
if their_coin_amount >= difference.unsigned_abs() {
// Add their coins to balance // Add their coins to balance
self.client.perform_trade_action(TradeAction::AddItem { self.client.perform_trade_action(TradeAction::AddItem {
item: their_coins, item: their_coins,
quantity: difference.unsigned_abs(), quantity: difference.unsigned_abs(),
ours: false, ours: false,
}); });
return Ok(());
} }
} }
let their_name = self.find_player_alias(&their_uid).unwrap().clone();
self.client.send_command(
"tell".to_string(),
vec![
their_name,
format!(
"The value of my offer is {my_offered_items_value} coins. You only have {their_coin_amount} coins.",
),
],
);
}
Ok(()) Ok(())
} }
@ -950,6 +1003,12 @@ impl Bot {
} }
} }
#[derive(Debug)]
pub struct Reciept {
pub my_items: HashMap<String, u32>,
pub their_items: HashMap<String, u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TradeMode { enum TradeMode {
AdminAccess, AdminAccess,